Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: WFS Translator
4 : * Purpose: Implements OGRWFSLayer class.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "ogr_wfs.h"
15 : #include "ogr_api.h"
16 : #include "cpl_minixml.h"
17 : #include "cpl_http.h"
18 : #include "parsexsd.h"
19 : #include "ogrwfsfilter.h"
20 :
21 : /************************************************************************/
22 : /* OGRWFSLayer() */
23 : /************************************************************************/
24 :
25 210 : OGRWFSLayer::OGRWFSLayer(OGRWFSDataSource *poDSIn, OGRSpatialReference *poSRSIn,
26 : int bAxisOrderAlreadyInvertedIn,
27 : const char *pszBaseURLIn, const char *pszNameIn,
28 210 : const char *pszNSIn, const char *pszNSValIn)
29 : : poDS(poDSIn), poFeatureDefn(nullptr), bGotApproximateLayerDefn(false),
30 : poGMLFeatureClass(nullptr),
31 : bAxisOrderAlreadyInverted(bAxisOrderAlreadyInvertedIn), m_poSRS(poSRSIn),
32 210 : pszBaseURL(CPLStrdup(pszBaseURLIn)), pszName(CPLStrdup(pszNameIn)),
33 210 : pszNS(pszNSIn ? CPLStrdup(pszNSIn) : nullptr),
34 210 : pszNSVal(pszNSValIn ? CPLStrdup(pszNSValIn) : nullptr),
35 : bStreamingDS(false), poBaseDS(nullptr), poBaseLayer(nullptr),
36 : bHasFetched(false), bReloadNeeded(false), eGeomType(wkbUnknown),
37 : nFeatures(-1), bCountFeaturesInGetNextFeature(false),
38 : poFetchedFilterGeom(nullptr), nExpectedInserts(0), bInTransaction(false),
39 : bUseFeatureIdAtLayerLevel(false), bPagingActive(false),
40 840 : nPagingStartIndex(0), nFeatureRead(0), pszRequiredOutputFormat(nullptr)
41 : {
42 210 : SetDescription(pszName);
43 :
44 : // If changing that, change in the GML driver too
45 210 : m_osTmpDir = VSIMemGenerateHiddenFilename("_ogr_wfs_");
46 210 : }
47 :
48 : /************************************************************************/
49 : /* Clone() */
50 : /************************************************************************/
51 :
52 0 : OGRWFSLayer *OGRWFSLayer::Clone()
53 : {
54 : OGRWFSLayer *poDupLayer =
55 0 : new OGRWFSLayer(poDS, m_poSRS, bAxisOrderAlreadyInverted, pszBaseURL,
56 0 : pszName, pszNS, pszNSVal);
57 0 : if (m_poSRS)
58 0 : m_poSRS->Reference();
59 0 : poDupLayer->poFeatureDefn = GetLayerDefn()->Clone();
60 0 : poDupLayer->poFeatureDefn->Reference();
61 0 : poDupLayer->bGotApproximateLayerDefn = bGotApproximateLayerDefn;
62 0 : poDupLayer->eGeomType = poDupLayer->poFeatureDefn->GetGeomType();
63 0 : poDupLayer->pszRequiredOutputFormat =
64 0 : pszRequiredOutputFormat ? CPLStrdup(pszRequiredOutputFormat) : nullptr;
65 :
66 : /* Copy existing schema file if already found */
67 0 : CPLString osSrcFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
68 : CPLString osTargetFileName =
69 0 : CPLSPrintf("%s/file.xsd", poDupLayer->m_osTmpDir.c_str());
70 0 : CPL_IGNORE_RET_VAL(CPLCopyFile(osTargetFileName, osSrcFileName));
71 :
72 0 : return poDupLayer;
73 : }
74 :
75 : /************************************************************************/
76 : /* ~OGRWFSLayer() */
77 : /************************************************************************/
78 :
79 420 : OGRWFSLayer::~OGRWFSLayer()
80 :
81 : {
82 210 : if (bInTransaction)
83 0 : OGRWFSLayer::CommitTransaction();
84 :
85 210 : if (m_poSRS != nullptr)
86 192 : m_poSRS->Release();
87 :
88 210 : if (poFeatureDefn != nullptr)
89 164 : poFeatureDefn->Release();
90 210 : delete poGMLFeatureClass;
91 :
92 210 : CPLFree(pszBaseURL);
93 210 : CPLFree(pszName);
94 210 : CPLFree(pszNS);
95 210 : CPLFree(pszNSVal);
96 :
97 210 : GDALClose(poBaseDS);
98 :
99 210 : delete poFetchedFilterGeom;
100 :
101 210 : VSIRmdirRecursive(m_osTmpDir.c_str());
102 :
103 210 : CPLFree(pszRequiredOutputFormat);
104 420 : }
105 :
106 : /************************************************************************/
107 : /* SetActiveSRS() */
108 : /************************************************************************/
109 :
110 4 : OGRErr OGRWFSLayer::SetActiveSRS(int /*iGeomField*/,
111 : const OGRSpatialReference *poSRS)
112 : {
113 4 : if (poSRS == nullptr)
114 1 : return OGRERR_FAILURE;
115 3 : const char *const apszOptions[] = {
116 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
117 3 : size_t i = 0;
118 9 : for (const auto &poSupportedSRS : m_apoSupportedCRSList)
119 : {
120 8 : if (poSupportedSRS->IsSame(poSRS, apszOptions))
121 : {
122 2 : m_osSRSName = m_aosSupportedCRSList[i];
123 2 : if (m_poSRS)
124 2 : m_poSRS->Release();
125 2 : m_poSRS = poSRS->Clone();
126 2 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
127 2 : if (poFeatureDefn)
128 : {
129 2 : auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
130 2 : if (poGeomFieldDefn)
131 : {
132 2 : poGeomFieldDefn->SetSpatialRef(m_poSRS);
133 : }
134 : }
135 2 : m_oExtents = OGREnvelope();
136 2 : if (m_oWGS84Extents.IsInit())
137 : {
138 4 : OGRSpatialReference oWGS84;
139 2 : oWGS84.SetWellKnownGeogCS("WGS84");
140 2 : oWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
141 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
142 4 : OGRCreateCoordinateTransformation(&oWGS84, m_poSRS));
143 2 : if (poCT)
144 : {
145 2 : poCT->TransformBounds(
146 : m_oWGS84Extents.MinX, m_oWGS84Extents.MinY,
147 : m_oWGS84Extents.MaxX, m_oWGS84Extents.MaxY,
148 : &m_oExtents.MinX, &m_oExtents.MinY, &m_oExtents.MaxX,
149 2 : &m_oExtents.MaxY, 20);
150 : }
151 : }
152 2 : SetSpatialFilter(nullptr);
153 2 : ResetReading();
154 2 : return OGRERR_NONE;
155 : }
156 6 : ++i;
157 : }
158 1 : return OGRERR_FAILURE;
159 : }
160 :
161 : /************************************************************************/
162 : /* GetDescribeFeatureTypeURL() */
163 : /************************************************************************/
164 :
165 164 : CPLString OGRWFSLayer::GetDescribeFeatureTypeURL(CPL_UNUSED int bWithNS)
166 : {
167 164 : CPLString osURL(pszBaseURL);
168 164 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
169 164 : osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion());
170 164 : osURL = CPLURLAddKVP(osURL, "REQUEST", "DescribeFeatureType");
171 164 : osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(pszName));
172 164 : osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", nullptr);
173 164 : osURL = CPLURLAddKVP(osURL, "MAXFEATURES", nullptr);
174 164 : osURL = CPLURLAddKVP(osURL, "COUNT", nullptr);
175 164 : osURL = CPLURLAddKVP(osURL, "FILTER", nullptr);
176 328 : osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT",
177 164 : pszRequiredOutputFormat
178 164 : ? WFS_EscapeURL(pszRequiredOutputFormat).c_str()
179 164 : : nullptr);
180 :
181 164 : if (pszNS && poDS->GetNeedNAMESPACE())
182 : {
183 : /* Older Deegree version require NAMESPACE (e.g.
184 : * http://www.nokis.org/deegree2/ogcwebservice) */
185 : /* This has been now corrected */
186 0 : CPLString osValue("xmlns(");
187 0 : osValue += pszNS;
188 0 : osValue += "=";
189 0 : osValue += pszNSVal;
190 0 : osValue += ")";
191 0 : osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue));
192 : }
193 :
194 164 : return osURL;
195 : }
196 :
197 : /************************************************************************/
198 : /* DescribeFeatureType() */
199 : /************************************************************************/
200 :
201 102 : OGRFeatureDefn *OGRWFSLayer::DescribeFeatureType()
202 : {
203 204 : CPLString osURL = GetDescribeFeatureTypeURL(TRUE);
204 :
205 102 : CPLDebug("WFS", "%s", osURL.c_str());
206 :
207 102 : CPLHTTPResult *psResult = poDS->HTTPFetch(osURL, nullptr);
208 102 : if (psResult == nullptr)
209 : {
210 15 : return nullptr;
211 : }
212 :
213 87 : if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
214 : nullptr)
215 : {
216 0 : if (poDS->IsOldDeegree((const char *)psResult->pabyData))
217 : {
218 0 : CPLHTTPDestroyResult(psResult);
219 0 : return DescribeFeatureType();
220 : }
221 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
222 : psResult->pabyData);
223 0 : CPLHTTPDestroyResult(psResult);
224 0 : return nullptr;
225 : }
226 :
227 87 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
228 87 : if (psXML == nullptr)
229 : {
230 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
231 : psResult->pabyData);
232 1 : CPLHTTPDestroyResult(psResult);
233 1 : return nullptr;
234 : }
235 86 : CPLHTTPDestroyResult(psResult);
236 :
237 86 : const CPLXMLNode *psSchema = WFSFindNode(psXML, "schema");
238 86 : if (psSchema == nullptr)
239 : {
240 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find <Schema>");
241 1 : CPLDestroyXMLNode(psXML);
242 :
243 1 : return nullptr;
244 : }
245 :
246 85 : OGRFeatureDefn *poFDefn = ParseSchema(psSchema);
247 85 : if (poFDefn)
248 79 : poDS->SaveLayerSchema(pszName, psSchema);
249 :
250 85 : CPLDestroyXMLNode(psXML);
251 85 : return poFDefn;
252 : }
253 :
254 : /************************************************************************/
255 : /* ParseSchema() */
256 : /************************************************************************/
257 :
258 147 : OGRFeatureDefn *OGRWFSLayer::ParseSchema(const CPLXMLNode *psSchema)
259 : {
260 147 : osTargetNamespace = CPLGetXMLValue(psSchema, "targetNamespace", "");
261 :
262 294 : CPLString osTmpFileName;
263 :
264 147 : osTmpFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
265 147 : CPLSerializeXMLTreeToFile(psSchema, osTmpFileName);
266 :
267 294 : std::vector<GMLFeatureClass *> aosClasses;
268 147 : bool bFullyUnderstood = false;
269 147 : bool bUseSchemaImports = false;
270 147 : bool bHaveSchema = GMLParseXSD(osTmpFileName, bUseSchemaImports, aosClasses,
271 : bFullyUnderstood);
272 :
273 147 : if (bHaveSchema && aosClasses.size() == 1)
274 : {
275 : // CPLDebug("WFS", "Creating %s for %s", osTmpFileName.c_str(),
276 : // GetName());
277 141 : return BuildLayerDefnFromFeatureClass(aosClasses[0]);
278 : }
279 6 : else if (bHaveSchema)
280 : {
281 : std::vector<GMLFeatureClass *>::const_iterator oIter =
282 0 : aosClasses.begin();
283 : std::vector<GMLFeatureClass *>::const_iterator oEndIter =
284 0 : aosClasses.end();
285 0 : while (oIter != oEndIter)
286 : {
287 0 : GMLFeatureClass *poClass = *oIter;
288 0 : ++oIter;
289 0 : delete poClass;
290 : }
291 : }
292 :
293 6 : VSIUnlink(osTmpFileName);
294 :
295 6 : return nullptr;
296 : }
297 :
298 : /************************************************************************/
299 : /* BuildLayerDefnFromFeatureClass() */
300 : /************************************************************************/
301 :
302 : OGRFeatureDefn *
303 141 : OGRWFSLayer::BuildLayerDefnFromFeatureClass(GMLFeatureClass *poClass)
304 : {
305 141 : poGMLFeatureClass = poClass;
306 :
307 141 : OGRFeatureDefn *poFDefn = new OGRFeatureDefn(pszName);
308 141 : poFDefn->SetGeomType(wkbNone);
309 141 : if (poGMLFeatureClass->GetGeometryPropertyCount() > 0)
310 : {
311 126 : poFDefn->SetGeomType(
312 126 : (OGRwkbGeometryType)poGMLFeatureClass->GetGeometryProperty(0)
313 126 : ->GetType());
314 126 : poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
315 : }
316 :
317 : /* -------------------------------------------------------------------- */
318 : /* Added attributes (properties). */
319 : /* -------------------------------------------------------------------- */
320 141 : if (poDS->ExposeGMLId())
321 : {
322 276 : OGRFieldDefn oField("gml_id", OFTString);
323 138 : oField.SetNullable(FALSE);
324 138 : poFDefn->AddFieldDefn(&oField);
325 : }
326 :
327 702 : for (int iField = 0; iField < poGMLFeatureClass->GetPropertyCount();
328 : iField++)
329 : {
330 561 : GMLPropertyDefn *poProperty = poGMLFeatureClass->GetProperty(iField);
331 561 : OGRFieldSubType eSubType = OFSTNone;
332 : const OGRFieldType eFType =
333 561 : GML_GetOGRFieldType(poProperty->GetType(), eSubType);
334 :
335 1122 : OGRFieldDefn oField(poProperty->GetName(), eFType);
336 561 : oField.SetSubType(eSubType);
337 561 : if (STARTS_WITH_CI(oField.GetNameRef(), "ogr:"))
338 0 : oField.SetName(poProperty->GetName() + 4);
339 561 : if (poProperty->GetWidth() > 0)
340 0 : oField.SetWidth(poProperty->GetWidth());
341 561 : if (poProperty->GetPrecision() > 0)
342 0 : oField.SetPrecision(poProperty->GetPrecision());
343 561 : if (!poDS->IsEmptyAsNull())
344 0 : oField.SetNullable(poProperty->IsNullable());
345 :
346 561 : poFDefn->AddFieldDefn(&oField);
347 : }
348 :
349 141 : if (poGMLFeatureClass->GetGeometryPropertyCount() > 0)
350 : {
351 : const char *pszGeometryColumnName =
352 126 : poGMLFeatureClass->GetGeometryProperty(0)->GetSrcElement();
353 126 : if (pszGeometryColumnName[0] != '\0')
354 : {
355 126 : osGeometryColumnName = pszGeometryColumnName;
356 126 : if (poFDefn->GetGeomFieldCount() > 0)
357 : {
358 252 : poFDefn->GetGeomFieldDefn(0)->SetNullable(
359 126 : poGMLFeatureClass->GetGeometryProperty(0)->IsNullable());
360 126 : poFDefn->GetGeomFieldDefn(0)->SetName(pszGeometryColumnName);
361 : }
362 : }
363 : }
364 :
365 141 : return poFDefn;
366 : }
367 :
368 : /************************************************************************/
369 : /* MakeGetFeatureURL() */
370 : /************************************************************************/
371 :
372 144 : CPLString OGRWFSLayer::MakeGetFeatureURL(int nRequestMaxFeatures,
373 : int bRequestHits)
374 : {
375 144 : CPLString osURL(pszBaseURL);
376 144 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
377 144 : osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion());
378 144 : osURL = CPLURLAddKVP(osURL, "REQUEST", "GetFeature");
379 144 : if (atoi(poDS->GetVersion()) >= 2)
380 34 : osURL = CPLURLAddKVP(osURL, "TYPENAMES", WFS_EscapeURL(pszName));
381 : else
382 110 : osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(pszName));
383 144 : if (!m_osSRSName.empty())
384 : osURL =
385 1 : CPLURLAddKVP(osURL, "SRSNAME", WFS_EscapeURL(m_osSRSName.c_str()));
386 144 : if (pszRequiredOutputFormat)
387 0 : osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT",
388 0 : WFS_EscapeURL(pszRequiredOutputFormat));
389 :
390 144 : if (poDS->IsPagingAllowed() && !bRequestHits)
391 : {
392 20 : nRequestMaxFeatures = poDS->GetPageSize();
393 : /* If the feature count is known and is less than the page size, we don't
394 : * need to do paging. Skipping the pagination parameters improves compatibility
395 : * with remote datasources that don't have a primary key.
396 : * Without a primary key, the WFS server can't support paging, since there
397 : * is no natural sort order defined. */
398 20 : if (nFeatures < 0 ||
399 6 : (nRequestMaxFeatures && nFeatures > nRequestMaxFeatures))
400 : {
401 : osURL =
402 36 : CPLURLAddKVP(osURL, "STARTINDEX",
403 18 : CPLSPrintf("%d", nPagingStartIndex +
404 36 : poDS->GetBaseStartIndex()));
405 18 : bPagingActive = true;
406 : }
407 : }
408 :
409 144 : if (nRequestMaxFeatures)
410 : {
411 129 : osURL = CPLURLAddKVP(
412 43 : osURL, atoi(poDS->GetVersion()) >= 2 ? "COUNT" : "MAXFEATURES",
413 43 : CPLSPrintf("%d", nRequestMaxFeatures));
414 : }
415 144 : if (pszNS && poDS->GetNeedNAMESPACE())
416 : {
417 : /* Older Deegree version require NAMESPACE (e.g.
418 : * http://www.nokis.org/deegree2/ogcwebservice) */
419 : /* This has been now corrected */
420 0 : CPLString osValue("xmlns(");
421 0 : osValue += pszNS;
422 0 : osValue += "=";
423 0 : osValue += pszNSVal;
424 0 : osValue += ")";
425 0 : osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue));
426 : }
427 :
428 144 : delete poFetchedFilterGeom;
429 144 : poFetchedFilterGeom = nullptr;
430 :
431 288 : CPLString osGeomFilter;
432 :
433 144 : if (m_poFilterGeom != nullptr && !osGeometryColumnName.empty())
434 : {
435 5 : OGREnvelope oEnvelope;
436 5 : m_poFilterGeom->getEnvelope(&oEnvelope);
437 :
438 5 : poFetchedFilterGeom = m_poFilterGeom->clone();
439 :
440 5 : osGeomFilter = "<BBOX>";
441 5 : if (atoi(poDS->GetVersion()) >= 2)
442 0 : osGeomFilter += "<ValueReference>";
443 : else
444 5 : osGeomFilter += "<PropertyName>";
445 5 : if (pszNS)
446 : {
447 0 : osGeomFilter += pszNS;
448 0 : osGeomFilter += ":";
449 : }
450 5 : osGeomFilter += osGeometryColumnName;
451 5 : if (atoi(poDS->GetVersion()) >= 2)
452 0 : osGeomFilter += "</ValueReference>";
453 : else
454 5 : osGeomFilter += "</PropertyName>";
455 :
456 5 : if (atoi(poDS->GetVersion()) >= 2)
457 : {
458 0 : osGeomFilter += "<gml:Envelope";
459 :
460 0 : CPLString osSRSName = CPLURLGetValue(pszBaseURL, "SRSNAME");
461 0 : if (!osSRSName.empty())
462 : {
463 0 : osGeomFilter += " srsName=\"";
464 0 : osGeomFilter += osSRSName;
465 0 : osGeomFilter += "\"";
466 : }
467 :
468 0 : osGeomFilter += ">";
469 0 : if (bAxisOrderAlreadyInverted)
470 : {
471 : osGeomFilter +=
472 : CPLSPrintf("<gml:lowerCorner>%.16f "
473 : "%.16f</gml:lowerCorner><gml:upperCorner>%.16f "
474 : "%.16f</gml:upperCorner>",
475 : oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY,
476 0 : oEnvelope.MaxX);
477 : }
478 : else
479 : osGeomFilter +=
480 : CPLSPrintf("<gml:lowerCorner>%.16f "
481 : "%.16f</gml:lowerCorner><gml:upperCorner>%.16f "
482 : "%.16f</gml:upperCorner>",
483 : oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX,
484 0 : oEnvelope.MaxY);
485 0 : osGeomFilter += "</gml:Envelope>";
486 : }
487 5 : else if (poDS->RequiresEnvelopeSpatialFilter())
488 : {
489 0 : osGeomFilter += "<Envelope xmlns=\"http://www.opengis.net/gml\">";
490 0 : if (bAxisOrderAlreadyInverted)
491 : {
492 : /* We can go here in WFS 1.1 with geographic coordinate systems
493 : */
494 : /* that are natively return in lat,long order, but as we have */
495 : /* presented long,lat order to the user, we must switch back */
496 : /* for the server... */
497 : osGeomFilter +=
498 : CPLSPrintf("<coord><X>%.16f</X><Y>%.16f</Y></"
499 : "coord><coord><X>%.16f</X><Y>%.16f</Y></coord>",
500 : oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY,
501 0 : oEnvelope.MaxX);
502 : }
503 : else
504 : osGeomFilter +=
505 : CPLSPrintf("<coord><X>%.16f</X><Y>%.16f</Y></"
506 : "coord><coord><X>%.16f</X><Y>%.16f</Y></coord>",
507 : oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX,
508 0 : oEnvelope.MaxY);
509 0 : osGeomFilter += "</Envelope>";
510 : }
511 : else
512 : {
513 5 : osGeomFilter += "<gml:Box>";
514 5 : osGeomFilter += "<gml:coordinates>";
515 5 : if (bAxisOrderAlreadyInverted)
516 : {
517 : /* We can go here in WFS 1.1 with geographic coordinate systems
518 : */
519 : /* that are natively return in lat,long order, but as we have */
520 : /* presented long,lat order to the user, we must switch back */
521 : /* for the server... */
522 : osGeomFilter +=
523 : CPLSPrintf("%.16f,%.16f %.16f,%.16f", oEnvelope.MinY,
524 5 : oEnvelope.MinX, oEnvelope.MaxY, oEnvelope.MaxX);
525 : }
526 : else
527 : osGeomFilter +=
528 : CPLSPrintf("%.16f,%.16f %.16f,%.16f", oEnvelope.MinX,
529 0 : oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY);
530 5 : osGeomFilter += "</gml:coordinates>";
531 5 : osGeomFilter += "</gml:Box>";
532 : }
533 5 : osGeomFilter += "</BBOX>";
534 : }
535 :
536 144 : if (!osGeomFilter.empty() || !osWFSWhere.empty())
537 : {
538 33 : CPLString osFilter;
539 33 : if (atoi(poDS->GetVersion()) >= 2)
540 0 : osFilter = "<Filter xmlns=\"http://www.opengis.net/fes/2.0\"";
541 : else
542 33 : osFilter = "<Filter xmlns=\"http://www.opengis.net/ogc\"";
543 33 : if (pszNS)
544 : {
545 0 : osFilter += " xmlns:";
546 0 : osFilter += pszNS;
547 0 : osFilter += "=\"";
548 0 : osFilter += pszNSVal;
549 0 : osFilter += "\"";
550 : }
551 33 : if (atoi(poDS->GetVersion()) >= 2)
552 0 : osFilter += " xmlns:gml=\"http://www.opengis.net/gml/3.2\">";
553 : else
554 33 : osFilter += " xmlns:gml=\"http://www.opengis.net/gml\">";
555 33 : if (!osGeomFilter.empty() && !osWFSWhere.empty())
556 2 : osFilter += "<And>";
557 33 : osFilter += osWFSWhere;
558 33 : osFilter += osGeomFilter;
559 33 : if (!osGeomFilter.empty() && !osWFSWhere.empty())
560 2 : osFilter += "</And>";
561 33 : osFilter += "</Filter>";
562 :
563 33 : osURL = CPLURLAddKVP(osURL, "FILTER", WFS_EscapeURL(osFilter));
564 : }
565 :
566 144 : if (bRequestHits)
567 : {
568 9 : osURL = CPLURLAddKVP(osURL, "RESULTTYPE", "hits");
569 : }
570 135 : else if (!aoSortColumns.empty())
571 : {
572 0 : CPLString osSortBy;
573 0 : for (int i = 0; i < (int)aoSortColumns.size(); i++)
574 : {
575 0 : if (i > 0)
576 0 : osSortBy += ",";
577 0 : osSortBy += aoSortColumns[i].osColumn;
578 0 : if (!aoSortColumns[i].bAsc)
579 : {
580 0 : if (atoi(poDS->GetVersion()) >= 2)
581 0 : osSortBy += " DESC";
582 : else
583 0 : osSortBy += " D";
584 : }
585 : }
586 0 : osURL = CPLURLAddKVP(osURL, "SORTBY", WFS_EscapeURL(osSortBy));
587 : }
588 :
589 : /* If no PROPERTYNAME is specified, build one if there are ignored fields */
590 288 : CPLString osPropertyName = CPLURLGetValue(osURL, "PROPERTYNAME");
591 144 : const char *pszPropertyName = osPropertyName.c_str();
592 144 : if (pszPropertyName[0] == 0 && poFeatureDefn != nullptr)
593 : {
594 135 : bool bHasIgnoredField = false;
595 135 : osPropertyName.clear();
596 897 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
597 : {
598 762 : if (EQUAL(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
599 : "gml_id"))
600 : {
601 : /* fake field : skip it */
602 : }
603 655 : else if (poFeatureDefn->GetFieldDefn(iField)->IsIgnored())
604 : {
605 24 : bHasIgnoredField = true;
606 : }
607 : else
608 : {
609 631 : if (!osPropertyName.empty())
610 528 : osPropertyName += ",";
611 : osPropertyName +=
612 631 : poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
613 : }
614 : }
615 135 : if (!osGeometryColumnName.empty())
616 : {
617 98 : if (poFeatureDefn->IsGeometryIgnored())
618 : {
619 0 : bHasIgnoredField = true;
620 : }
621 : else
622 : {
623 98 : if (!osPropertyName.empty())
624 96 : osPropertyName += ",";
625 98 : osPropertyName += osGeometryColumnName;
626 : }
627 : }
628 :
629 135 : if (bHasIgnoredField && !osPropertyName.empty())
630 : {
631 8 : osURL = CPLURLAddKVP(osURL, "PROPERTYNAME",
632 12 : WFS_EscapeURL(osPropertyName));
633 : }
634 : }
635 :
636 288 : return osURL;
637 : }
638 :
639 : /************************************************************************/
640 : /* OGRWFSFetchContentDispositionFilename() */
641 : /************************************************************************/
642 :
643 50 : static const char *OGRWFSFetchContentDispositionFilename(char **papszHeaders)
644 : {
645 : const char *pszContentDisposition =
646 50 : CSLFetchNameValue(papszHeaders, "Content-Disposition");
647 50 : if (pszContentDisposition &&
648 6 : STARTS_WITH(pszContentDisposition, "attachment; filename="))
649 : {
650 6 : return pszContentDisposition + strlen("attachment; filename=");
651 : }
652 44 : return nullptr;
653 : }
654 :
655 : /************************************************************************/
656 : /* MustRetryIfNonCompliantServer() */
657 : /************************************************************************/
658 :
659 50 : bool OGRWFSLayer::MustRetryIfNonCompliantServer(const char *pszServerAnswer)
660 : {
661 50 : bool bRetry = false;
662 :
663 : /* Deegree server does not support PropertyIsNotEqualTo */
664 : /* We have to turn it into <Not><PropertyIsEqualTo> */
665 63 : if (!osWFSWhere.empty() && poDS->PropertyIsNotEqualToSupported() &&
666 13 : strstr(pszServerAnswer,
667 : "Unknown comparison operation: 'PropertyIsNotEqualTo'") !=
668 : nullptr)
669 : {
670 0 : poDS->SetPropertyIsNotEqualToUnSupported();
671 0 : bRetry = true;
672 : }
673 :
674 : /* Deegree server requires the gml: prefix in GmlObjectId element, but ESRI
675 : */
676 : /* doesn't like it at all ! Other servers don't care... */
677 63 : if (!osWFSWhere.empty() && !poDS->DoesGmlObjectIdNeedGMLPrefix() &&
678 13 : strstr(pszServerAnswer,
679 : "<GmlObjectId> requires 'gml:id'-attribute!") != nullptr)
680 : {
681 0 : poDS->SetGmlObjectIdNeedsGMLPrefix();
682 0 : bRetry = true;
683 : }
684 :
685 : /* GeoServer can return the error 'Only FeatureIds are supported when
686 : * encoding id filters to SDE' */
687 63 : if (!osWFSWhere.empty() && !bUseFeatureIdAtLayerLevel &&
688 13 : strstr(pszServerAnswer, "Only FeatureIds are supported") != nullptr)
689 : {
690 0 : bUseFeatureIdAtLayerLevel = true;
691 0 : bRetry = true;
692 : }
693 :
694 50 : if (bRetry)
695 : {
696 0 : SetAttributeFilter(osSQLWhere);
697 0 : bHasFetched = true;
698 0 : bReloadNeeded = false;
699 : }
700 :
701 50 : return bRetry;
702 : }
703 :
704 : /************************************************************************/
705 : /* FetchGetFeature() */
706 : /************************************************************************/
707 :
708 119 : GDALDataset *OGRWFSLayer::FetchGetFeature(int nRequestMaxFeatures)
709 : {
710 :
711 238 : CPLString osURL = MakeGetFeatureURL(nRequestMaxFeatures, FALSE);
712 119 : CPLDebug("WFS", "%s", osURL.c_str());
713 :
714 119 : CPLHTTPResult *psResult = nullptr;
715 :
716 238 : CPLString osOutputFormat = CPLURLGetValue(osURL, "OUTPUTFORMAT");
717 :
718 82 : const auto ReadNumberMatched = [this](const char *pszData)
719 : {
720 74 : const char *pszNumberMatched = strstr(pszData, " numberMatched=\"");
721 74 : if (!pszNumberMatched)
722 69 : pszNumberMatched = strstr(pszData, "\n"
723 : "numberMatched=\"");
724 74 : if (pszNumberMatched)
725 : {
726 17 : pszNumberMatched += strlen(" numberMatched=\"");
727 17 : if (*pszNumberMatched >= '0' && *pszNumberMatched <= '9')
728 : {
729 2 : m_nNumberMatched = CPLAtoGIntBig(pszNumberMatched);
730 2 : CPLDebug("WFS", "numberMatched = " CPL_FRMT_GIB,
731 : m_nNumberMatched);
732 2 : if (!bCountFeaturesInGetNextFeature)
733 2 : nFeatures = m_nNumberMatched;
734 : }
735 : }
736 193 : };
737 :
738 119 : if (CPLTestBool(CPLGetConfigOption("OGR_WFS_USE_STREAMING", "YES")))
739 : {
740 68 : CPLString osStreamingName;
741 133 : if (STARTS_WITH(osURL, "/vsimem/") &&
742 65 : CPLTestBool(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")))
743 : {
744 65 : osStreamingName = osURL;
745 : }
746 : else
747 : {
748 3 : osStreamingName += "/vsicurl_streaming/";
749 3 : osStreamingName += osURL;
750 : }
751 :
752 : /* Try streaming when the output format is GML and that we have a .xsd
753 : */
754 : /* that we are able to understand */
755 68 : CPLString osXSDFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
756 : VSIStatBufL sBuf;
757 68 : GDALDriver *poDriver = nullptr;
758 68 : if ((osOutputFormat.empty() ||
759 6 : osOutputFormat.ifind("GML") != std::string::npos) &&
760 106 : VSIStatL(osXSDFileName, &sBuf) == 0 &&
761 44 : (poDriver = GDALDriver::FromHandle(GDALGetDriverByName("GML"))) !=
762 136 : nullptr &&
763 44 : poDriver->pfnOpen)
764 : {
765 44 : bStreamingDS = true;
766 44 : CPLStringList aosOptions;
767 44 : aosOptions.SetNameValue("XSD", osXSDFileName.c_str());
768 : aosOptions.SetNameValue("EMPTY_AS_NULL",
769 44 : poDS->IsEmptyAsNull() ? "YES" : "NO");
770 44 : if (CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG",
771 44 : nullptr) == nullptr)
772 : {
773 : aosOptions.SetNameValue(
774 : "INVERT_AXIS_ORDER_IF_LAT_LONG",
775 44 : poDS->InvertAxisOrderIfLatLong() ? "YES" : "NO");
776 : }
777 44 : if (CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", nullptr) ==
778 : nullptr)
779 : {
780 : aosOptions.SetNameValue("CONSIDER_EPSG_AS_URN",
781 44 : poDS->GetConsiderEPSGAsURN().c_str());
782 : }
783 44 : if (CPLGetConfigOption("GML_EXPOSE_GML_ID", nullptr) == nullptr)
784 : {
785 : aosOptions.SetNameValue("EXPOSE_GML_ID",
786 44 : poDS->ExposeGMLId() ? "YES" : "NO");
787 : // iGMLOOIdex ++;
788 : }
789 :
790 44 : GDALOpenInfo oOpenInfo(osStreamingName.c_str(), GA_ReadOnly);
791 44 : if (oOpenInfo.nHeaderBytes && m_nNumberMatched < 0)
792 : {
793 29 : const char *pszData =
794 : reinterpret_cast<const char *>(oOpenInfo.pabyHeader);
795 29 : ReadNumberMatched(pszData);
796 : }
797 44 : oOpenInfo.papszOpenOptions = aosOptions.List();
798 :
799 44 : auto poOutputDS = poDriver->Open(&oOpenInfo, true);
800 44 : if (poOutputDS)
801 : {
802 29 : return poOutputDS;
803 : }
804 : }
805 : /* Try streaming when the output format is FlatGeobuf */
806 24 : else if ((osOutputFormat.empty() ||
807 6 : osOutputFormat.ifind("flatgeobuf") != std::string::npos) &&
808 31 : VSIStatL(osXSDFileName, &sBuf) == 0 &&
809 1 : GDALGetDriverByName("FlatGeobuf") != nullptr)
810 : {
811 1 : bStreamingDS = true;
812 1 : const char *const apszAllowedDrivers[] = {"FlatGeobuf", nullptr};
813 :
814 : GDALDataset *poFlatGeobuf_DS =
815 1 : (GDALDataset *)GDALOpenEx(osStreamingName, GDAL_OF_VECTOR,
816 : apszAllowedDrivers, nullptr, nullptr);
817 1 : if (poFlatGeobuf_DS)
818 : {
819 1 : return poFlatGeobuf_DS;
820 : }
821 : }
822 : else
823 : {
824 23 : bStreamingDS = false;
825 : }
826 :
827 38 : if (bStreamingDS)
828 : {
829 : /* In case of failure, read directly the content to examine */
830 : /* it, if it is XML error content */
831 : char szBuffer[2048];
832 15 : int nRead = 0;
833 15 : VSILFILE *fp = VSIFOpenL(osStreamingName, "rb");
834 15 : if (fp)
835 : {
836 2 : nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp);
837 2 : szBuffer[nRead] = '\0';
838 2 : VSIFCloseL(fp);
839 : }
840 :
841 15 : if (nRead != 0)
842 : {
843 2 : if (MustRetryIfNonCompliantServer(szBuffer))
844 1 : return FetchGetFeature(nRequestMaxFeatures);
845 :
846 2 : if (strstr(szBuffer, "<ServiceExceptionReport") != nullptr ||
847 1 : strstr(szBuffer, "<ows:ExceptionReport") != nullptr)
848 : {
849 1 : if (poDS->IsOldDeegree(szBuffer))
850 : {
851 0 : return FetchGetFeature(nRequestMaxFeatures);
852 : }
853 :
854 1 : CPLError(CE_Failure, CPLE_AppDefined,
855 : "Error returned by server : %s", szBuffer);
856 1 : return nullptr;
857 : }
858 : }
859 : }
860 : }
861 :
862 88 : bStreamingDS = false;
863 88 : psResult = poDS->HTTPFetch(osURL, nullptr);
864 88 : if (psResult == nullptr)
865 : {
866 40 : return nullptr;
867 : }
868 :
869 48 : const char *pszContentType = "";
870 48 : if (psResult->pszContentType)
871 6 : pszContentType = psResult->pszContentType;
872 :
873 48 : VSIMkdir(m_osTmpDir.c_str(), 0);
874 :
875 48 : GByte *pabyData = psResult->pabyData;
876 48 : int nDataLen = psResult->nDataLen;
877 48 : bool bIsMultiPart = false;
878 48 : const char *pszAttachmentFilename = nullptr;
879 :
880 54 : if (strstr(pszContentType, "multipart") &&
881 6 : CPLHTTPParseMultipartMime(psResult))
882 : {
883 6 : bIsMultiPart = true;
884 6 : VSIRmdirRecursive(m_osTmpDir.c_str());
885 6 : VSIMkdir(m_osTmpDir.c_str(), 0);
886 14 : for (int i = 0; i < psResult->nMimePartCount; i++)
887 : {
888 16 : CPLString osTmpFileName = m_osTmpDir + "/";
889 16 : pszAttachmentFilename = OGRWFSFetchContentDispositionFilename(
890 8 : psResult->pasMimePart[i].papszHeaders);
891 :
892 8 : if (pszAttachmentFilename)
893 6 : osTmpFileName += pszAttachmentFilename;
894 : else
895 2 : osTmpFileName += CPLSPrintf("file_%d", i);
896 :
897 : GByte *pData =
898 8 : (GByte *)VSI_MALLOC_VERBOSE(psResult->pasMimePart[i].nDataLen);
899 8 : if (pData)
900 : {
901 8 : memcpy(pData, psResult->pasMimePart[i].pabyData,
902 8 : psResult->pasMimePart[i].nDataLen);
903 8 : VSILFILE *fp = VSIFileFromMemBuffer(
904 8 : osTmpFileName, pData, psResult->pasMimePart[i].nDataLen,
905 : TRUE);
906 8 : VSIFCloseL(fp);
907 : }
908 : }
909 : }
910 : else
911 : pszAttachmentFilename =
912 42 : OGRWFSFetchContentDispositionFilename(psResult->papszHeaders);
913 :
914 48 : bool bJSON = false;
915 48 : bool bCSV = false;
916 48 : bool bKML = false;
917 48 : bool bKMZ = false;
918 48 : bool bFlatGeobuf = false;
919 48 : bool bZIP = false;
920 48 : bool bGZIP = false;
921 :
922 48 : const char *pszOutputFormat = osOutputFormat.c_str();
923 :
924 96 : if (FindSubStringInsensitive(pszContentType, "json") ||
925 48 : FindSubStringInsensitive(pszOutputFormat, "json"))
926 : {
927 4 : bJSON = true;
928 : }
929 88 : else if (FindSubStringInsensitive(pszContentType, "csv") ||
930 44 : FindSubStringInsensitive(pszOutputFormat, "csv"))
931 : {
932 0 : bCSV = true;
933 : }
934 88 : else if (FindSubStringInsensitive(pszContentType, "kml") ||
935 44 : FindSubStringInsensitive(pszOutputFormat, "kml"))
936 : {
937 0 : bKML = true;
938 : }
939 88 : else if (FindSubStringInsensitive(pszContentType, "kmz") ||
940 44 : FindSubStringInsensitive(pszOutputFormat, "kmz"))
941 : {
942 0 : bKMZ = true;
943 : }
944 88 : else if (FindSubStringInsensitive(pszContentType, "flatgeobuf") ||
945 44 : FindSubStringInsensitive(pszOutputFormat, "flatgeobuf"))
946 : {
947 0 : bFlatGeobuf = true;
948 : }
949 44 : else if (strstr(pszContentType, "application/zip") != nullptr)
950 : {
951 0 : bZIP = true;
952 : }
953 44 : else if (strstr(pszContentType, "application/gzip") != nullptr)
954 : {
955 0 : bGZIP = true;
956 : }
957 :
958 48 : const char *pszData = reinterpret_cast<const char *>(pabyData);
959 48 : if (MustRetryIfNonCompliantServer(pszData))
960 : {
961 0 : CPLHTTPDestroyResult(psResult);
962 0 : return FetchGetFeature(nRequestMaxFeatures);
963 : }
964 :
965 48 : if (strstr(pszData, "<ServiceExceptionReport") != nullptr ||
966 47 : strstr(pszData, "<ows:ExceptionReport") != nullptr)
967 : {
968 1 : if (poDS->IsOldDeegree(pszData))
969 : {
970 0 : CPLHTTPDestroyResult(psResult);
971 0 : return FetchGetFeature(nRequestMaxFeatures);
972 : }
973 :
974 1 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
975 : pabyData);
976 1 : CPLHTTPDestroyResult(psResult);
977 1 : return nullptr;
978 : }
979 :
980 47 : if (m_nNumberMatched < 0)
981 45 : ReadNumberMatched(pszData);
982 :
983 94 : CPLString osTmpFileName;
984 :
985 47 : if (!bIsMultiPart)
986 : {
987 41 : if (bJSON)
988 4 : osTmpFileName = m_osTmpDir + "/file.geojson";
989 37 : else if (bZIP)
990 0 : osTmpFileName = m_osTmpDir + "/file.zip";
991 37 : else if (bCSV)
992 0 : osTmpFileName = m_osTmpDir + "/file.csv";
993 37 : else if (bKML)
994 0 : osTmpFileName = m_osTmpDir + "/file.kml";
995 37 : else if (bKMZ)
996 0 : osTmpFileName = m_osTmpDir + "/file.kmz";
997 37 : else if (bFlatGeobuf)
998 0 : osTmpFileName = m_osTmpDir + "/file.fgb";
999 : /* GML is a special case. It needs the .xsd file that has been saved */
1000 : /* as file.xsd, so we cannot used the attachment filename */
1001 37 : else if (pszAttachmentFilename &&
1002 37 : !EQUAL(CPLGetExtensionSafe(pszAttachmentFilename).c_str(),
1003 : "GML"))
1004 : {
1005 0 : osTmpFileName = m_osTmpDir + "/";
1006 0 : osTmpFileName += pszAttachmentFilename;
1007 : }
1008 : else
1009 : {
1010 37 : osTmpFileName = m_osTmpDir + "/file.gfs";
1011 37 : VSIUnlink(osTmpFileName);
1012 :
1013 37 : osTmpFileName = m_osTmpDir + "/file.gml";
1014 : }
1015 :
1016 : VSILFILE *fp =
1017 41 : VSIFileFromMemBuffer(osTmpFileName, pabyData, nDataLen, TRUE);
1018 41 : VSIFCloseL(fp);
1019 41 : psResult->pabyData = nullptr;
1020 :
1021 41 : if (bZIP)
1022 : {
1023 0 : osTmpFileName = "/vsizip/" + osTmpFileName;
1024 : }
1025 41 : else if (bGZIP)
1026 : {
1027 0 : osTmpFileName = "/vsigzip/" + osTmpFileName;
1028 : }
1029 : }
1030 : else
1031 : {
1032 6 : pabyData = nullptr;
1033 6 : nDataLen = 0;
1034 6 : osTmpFileName = m_osTmpDir;
1035 : }
1036 :
1037 47 : CPLHTTPDestroyResult(psResult);
1038 :
1039 47 : const char *const *papszOpenOptions = nullptr;
1040 47 : const char *apszGMLOpenOptions[4] = {nullptr, nullptr, nullptr, nullptr};
1041 47 : int iGMLOOIdex = 0;
1042 47 : if (CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", nullptr) ==
1043 : nullptr)
1044 : {
1045 47 : apszGMLOpenOptions[iGMLOOIdex] =
1046 47 : CPLSPrintf("INVERT_AXIS_ORDER_IF_LAT_LONG=%s",
1047 47 : poDS->InvertAxisOrderIfLatLong() ? "YES" : "NO");
1048 47 : iGMLOOIdex++;
1049 : }
1050 47 : if (CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", nullptr) == nullptr)
1051 : {
1052 47 : apszGMLOpenOptions[iGMLOOIdex] = CPLSPrintf(
1053 47 : "CONSIDER_EPSG_AS_URN=%s", poDS->GetConsiderEPSGAsURN().c_str());
1054 47 : iGMLOOIdex++;
1055 : }
1056 47 : if (CPLGetConfigOption("GML_EXPOSE_GML_ID", nullptr) == nullptr)
1057 : {
1058 47 : apszGMLOpenOptions[iGMLOOIdex] =
1059 47 : CPLSPrintf("EXPOSE_GML_ID=%s", poDS->ExposeGMLId() ? "YES" : "NO");
1060 : // iGMLOOIdex ++;
1061 : }
1062 :
1063 47 : GDALDriverH hDrv = GDALIdentifyDriver(osTmpFileName, nullptr);
1064 47 : if (hDrv != nullptr && hDrv == GDALGetDriverByName("GML"))
1065 35 : papszOpenOptions = apszGMLOpenOptions;
1066 :
1067 47 : GDALDataset *poPageDS = (GDALDataset *)GDALOpenEx(
1068 : osTmpFileName, GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr);
1069 47 : if (poPageDS == nullptr && (bZIP || bIsMultiPart))
1070 : {
1071 4 : char **papszFileList = VSIReadDir(osTmpFileName);
1072 4 : for (int i = 0; papszFileList != nullptr && papszFileList[i] != nullptr;
1073 : i++)
1074 : {
1075 : const CPLString osFullFilename =
1076 4 : CPLFormFilenameSafe(osTmpFileName, papszFileList[i], nullptr);
1077 4 : hDrv = GDALIdentifyDriver(osFullFilename, nullptr);
1078 4 : if (hDrv != nullptr && hDrv == GDALGetDriverByName("GML"))
1079 0 : papszOpenOptions = apszGMLOpenOptions;
1080 : poPageDS =
1081 4 : (GDALDataset *)GDALOpenEx(osFullFilename, GDAL_OF_VECTOR,
1082 : nullptr, papszOpenOptions, nullptr);
1083 4 : if (poPageDS != nullptr)
1084 4 : break;
1085 : }
1086 :
1087 4 : CSLDestroy(papszFileList);
1088 : }
1089 :
1090 47 : if (poPageDS == nullptr)
1091 : {
1092 2 : if (pabyData != nullptr && !bJSON && !bZIP &&
1093 2 : strstr((const char *)pabyData, "<wfs:FeatureCollection") ==
1094 2 : nullptr &&
1095 2 : strstr((const char *)pabyData, "<gml:FeatureCollection") == nullptr)
1096 : {
1097 2 : if (nDataLen > 1000)
1098 0 : pabyData[1000] = 0;
1099 2 : CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
1100 : pabyData);
1101 : }
1102 2 : return nullptr;
1103 : }
1104 :
1105 45 : OGRLayer *poLayer = poPageDS->GetLayer(0);
1106 45 : if (poLayer == nullptr)
1107 : {
1108 0 : GDALClose(poPageDS);
1109 0 : return nullptr;
1110 : }
1111 :
1112 45 : return poPageDS;
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* GetLayerDefn() */
1117 : /************************************************************************/
1118 :
1119 2047 : OGRFeatureDefn *OGRWFSLayer::GetLayerDefn()
1120 : {
1121 2047 : if (poFeatureDefn)
1122 1917 : return poFeatureDefn;
1123 :
1124 130 : if (poDS->GetLayerCount() > 1)
1125 : {
1126 53 : poDS->LoadMultipleLayerDefn(GetName(), pszNS, pszNSVal);
1127 :
1128 53 : if (poFeatureDefn)
1129 28 : return poFeatureDefn;
1130 : }
1131 :
1132 102 : return BuildLayerDefn();
1133 : }
1134 :
1135 : /************************************************************************/
1136 : /* BuildLayerDefn() */
1137 : /************************************************************************/
1138 :
1139 164 : OGRFeatureDefn *OGRWFSLayer::BuildLayerDefn(OGRFeatureDefn *poSrcFDefn)
1140 : {
1141 164 : bool bUnsetWidthPrecision = false;
1142 :
1143 164 : poFeatureDefn = new OGRFeatureDefn(pszName);
1144 164 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
1145 164 : poFeatureDefn->Reference();
1146 :
1147 164 : GDALDataset *l_poDS = nullptr;
1148 :
1149 164 : if (poSrcFDefn == nullptr)
1150 102 : poSrcFDefn = DescribeFeatureType();
1151 164 : if (poSrcFDefn == nullptr)
1152 : {
1153 23 : l_poDS = FetchGetFeature(1);
1154 23 : if (l_poDS == nullptr)
1155 : {
1156 20 : return poFeatureDefn;
1157 : }
1158 3 : OGRLayer *l_poLayer = l_poDS->GetLayer(0);
1159 3 : if (l_poLayer == nullptr)
1160 : {
1161 0 : return poFeatureDefn;
1162 : }
1163 3 : poSrcFDefn = l_poLayer->GetLayerDefn();
1164 3 : bGotApproximateLayerDefn = true;
1165 : /* We cannot trust width and precision based on a single feature */
1166 3 : bUnsetWidthPrecision = true;
1167 : }
1168 :
1169 : const CPLStringList aosPropertyName(CSLTokenizeString2(
1170 144 : CPLURLGetValue(pszBaseURL, "PROPERTYNAME"), "(,)", 0));
1171 :
1172 144 : poFeatureDefn->SetGeomType(poSrcFDefn->GetGeomType());
1173 144 : if (poSrcFDefn->GetGeomFieldCount() > 0)
1174 258 : poFeatureDefn->GetGeomFieldDefn(0)->SetName(
1175 129 : poSrcFDefn->GetGeomFieldDefn(0)->GetNameRef());
1176 861 : for (int i = 0; i < poSrcFDefn->GetFieldCount(); i++)
1177 : {
1178 717 : if (!aosPropertyName.empty())
1179 : {
1180 0 : if (aosPropertyName.FindString(
1181 0 : poSrcFDefn->GetFieldDefn(i)->GetNameRef()) >= 0)
1182 0 : poFeatureDefn->AddFieldDefn(poSrcFDefn->GetFieldDefn(i));
1183 : else
1184 0 : bGotApproximateLayerDefn = true;
1185 : }
1186 : else
1187 : {
1188 1434 : OGRFieldDefn oFieldDefn(poSrcFDefn->GetFieldDefn(i));
1189 717 : if (bUnsetWidthPrecision)
1190 : {
1191 18 : oFieldDefn.SetWidth(0);
1192 18 : oFieldDefn.SetPrecision(0);
1193 : }
1194 717 : poFeatureDefn->AddFieldDefn(&oFieldDefn);
1195 : }
1196 : }
1197 :
1198 144 : if (l_poDS)
1199 3 : GDALClose(l_poDS);
1200 : else
1201 141 : delete poSrcFDefn;
1202 :
1203 144 : return poFeatureDefn;
1204 : }
1205 :
1206 : /************************************************************************/
1207 : /* ResetReading() */
1208 : /************************************************************************/
1209 :
1210 200 : void OGRWFSLayer::ResetReading()
1211 :
1212 : {
1213 200 : if (poFeatureDefn == nullptr)
1214 7 : return;
1215 193 : if (bPagingActive)
1216 0 : bReloadNeeded = true;
1217 193 : nPagingStartIndex = 0;
1218 193 : nFeatureRead = 0;
1219 193 : m_nNumberMatched = -1;
1220 193 : m_bHasReadAtLeastOneFeatureInThisPage = false;
1221 193 : if (bReloadNeeded)
1222 : {
1223 41 : GDALClose(poBaseDS);
1224 41 : poBaseDS = nullptr;
1225 41 : poBaseLayer = nullptr;
1226 41 : bHasFetched = false;
1227 41 : bReloadNeeded = false;
1228 : }
1229 193 : if (poBaseLayer)
1230 52 : poBaseLayer->ResetReading();
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* SetIgnoredFields() */
1235 : /************************************************************************/
1236 :
1237 16 : OGRErr OGRWFSLayer::SetIgnoredFields(CSLConstList papszFields)
1238 : {
1239 16 : bReloadNeeded = true;
1240 16 : ResetReading();
1241 16 : return OGRLayer::SetIgnoredFields(papszFields);
1242 : }
1243 :
1244 : /************************************************************************/
1245 : /* GetNextFeature() */
1246 : /************************************************************************/
1247 :
1248 126 : OGRFeature *OGRWFSLayer::GetNextFeature()
1249 : {
1250 126 : GetLayerDefn();
1251 :
1252 : while (true)
1253 : {
1254 139 : if (bReloadNeeded)
1255 : {
1256 40 : m_bHasReadAtLeastOneFeatureInThisPage = false;
1257 40 : GDALClose(poBaseDS);
1258 40 : poBaseDS = nullptr;
1259 40 : poBaseLayer = nullptr;
1260 40 : bHasFetched = false;
1261 40 : bReloadNeeded = false;
1262 : }
1263 139 : if (poBaseDS == nullptr && !bHasFetched)
1264 : {
1265 96 : bHasFetched = true;
1266 96 : poBaseDS = FetchGetFeature(0);
1267 96 : poBaseLayer = nullptr;
1268 96 : if (poBaseDS)
1269 : {
1270 72 : poBaseLayer = poBaseDS->GetLayer(0);
1271 72 : if (poBaseLayer == nullptr)
1272 0 : return nullptr;
1273 72 : poBaseLayer->ResetReading();
1274 :
1275 : /* Check that the layer field definition is consistent with the
1276 : * one */
1277 : /* we got in BuildLayerDefn() */
1278 72 : if (poFeatureDefn->GetFieldCount() !=
1279 72 : poBaseLayer->GetLayerDefn()->GetFieldCount())
1280 6 : bGotApproximateLayerDefn = true;
1281 : else
1282 : {
1283 502 : for (int iField = 0;
1284 502 : iField < poFeatureDefn->GetFieldCount(); iField++)
1285 : {
1286 : OGRFieldDefn *poFDefn1 =
1287 442 : poFeatureDefn->GetFieldDefn(iField);
1288 : OGRFieldDefn *poFDefn2 =
1289 442 : poBaseLayer->GetLayerDefn()->GetFieldDefn(iField);
1290 442 : if (strcmp(poFDefn1->GetNameRef(),
1291 878 : poFDefn2->GetNameRef()) != 0 ||
1292 436 : poFDefn1->GetType() != poFDefn2->GetType())
1293 : {
1294 6 : bGotApproximateLayerDefn = true;
1295 6 : break;
1296 : }
1297 : }
1298 : }
1299 : }
1300 : }
1301 139 : if (poBaseDS == nullptr || poBaseLayer == nullptr)
1302 37 : return nullptr;
1303 :
1304 102 : OGRFeature *poSrcFeature = poBaseLayer->GetNextFeature();
1305 102 : if (poSrcFeature == nullptr)
1306 : {
1307 24 : if (bPagingActive && m_bHasReadAtLeastOneFeatureInThisPage &&
1308 14 : (m_nNumberMatched < 0 || nFeatureRead < m_nNumberMatched))
1309 : {
1310 12 : bReloadNeeded = true;
1311 12 : nPagingStartIndex = nFeatureRead;
1312 12 : continue;
1313 : }
1314 12 : return nullptr;
1315 : }
1316 78 : nFeatureRead++;
1317 78 : m_bHasReadAtLeastOneFeatureInThisPage = true;
1318 78 : if (bCountFeaturesInGetNextFeature)
1319 2 : nFeatures++;
1320 :
1321 78 : OGRGeometry *poGeom = poSrcFeature->GetGeometryRef();
1322 84 : if (m_poFilterGeom != nullptr && poGeom != nullptr &&
1323 6 : !FilterGeometry(poGeom))
1324 : {
1325 0 : delete poSrcFeature;
1326 0 : continue;
1327 : }
1328 :
1329 : /* Client-side attribute filtering with underlying layer defn */
1330 : /* identical to exposed layer defn. */
1331 64 : if (!bGotApproximateLayerDefn && osWFSWhere.empty() &&
1332 142 : m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poSrcFeature))
1333 : {
1334 0 : delete poSrcFeature;
1335 0 : continue;
1336 : }
1337 :
1338 78 : OGRFeature *poNewFeature = new OGRFeature(poFeatureDefn);
1339 78 : if (bGotApproximateLayerDefn)
1340 : {
1341 14 : poNewFeature->SetFrom(poSrcFeature);
1342 14 : poNewFeature->SetFID(poSrcFeature->GetFID());
1343 :
1344 : /* Client-side attribute filtering. */
1345 16 : if (m_poAttrQuery != nullptr && osWFSWhere.empty() &&
1346 2 : !m_poAttrQuery->Evaluate(poNewFeature))
1347 : {
1348 1 : delete poSrcFeature;
1349 1 : delete poNewFeature;
1350 1 : continue;
1351 : }
1352 : }
1353 : else
1354 : {
1355 64 : poNewFeature->SetFID(poSrcFeature->GetFID());
1356 524 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount();
1357 : iField++)
1358 : {
1359 460 : poNewFeature->SetField(iField,
1360 460 : poSrcFeature->GetRawFieldRef(iField));
1361 : }
1362 64 : poNewFeature->SetStyleString(poSrcFeature->GetStyleString());
1363 64 : poNewFeature->SetGeometryDirectly(poSrcFeature->StealGeometry());
1364 : }
1365 77 : poGeom = poNewFeature->GetGeometryRef();
1366 :
1367 : /* FIXME? I don't really know what we should do with WFS 1.1.0 */
1368 : /* and non-GML format !!! I guess 50% WFS servers must do it wrong
1369 : * anyway */
1370 : /* GeoServer does currently axis inversion for non GML output, but */
1371 : /* apparently this is not correct :
1372 : * http://jira.codehaus.org/browse/GEOS-3657 */
1373 118 : if (poGeom != nullptr && bAxisOrderAlreadyInverted &&
1374 41 : strcmp(poBaseDS->GetDriverName(), "GML") != 0)
1375 : {
1376 9 : poGeom->swapXY();
1377 : }
1378 :
1379 77 : if (poGeom && m_poSRS)
1380 43 : poGeom->assignSpatialReference(m_poSRS);
1381 77 : delete poSrcFeature;
1382 77 : return poNewFeature;
1383 13 : }
1384 : }
1385 :
1386 : /************************************************************************/
1387 : /* ISetSpatialFilter() */
1388 : /************************************************************************/
1389 :
1390 29 : OGRErr OGRWFSLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
1391 : {
1392 29 : if (bStreamingDS)
1393 : {
1394 7 : bReloadNeeded = true;
1395 : }
1396 22 : else if (poFetchedFilterGeom == nullptr && poBaseDS != nullptr)
1397 : {
1398 : /* If there was no filter set, and that we set one */
1399 : /* the new result set can only be a subset of the whole */
1400 : /* so no need to reload from source */
1401 5 : bReloadNeeded = false;
1402 : }
1403 17 : else if (poFetchedFilterGeom != nullptr && poGeom != nullptr &&
1404 2 : poBaseDS != nullptr)
1405 : {
1406 1 : OGREnvelope oOldEnvelope, oNewEnvelope;
1407 1 : poFetchedFilterGeom->getEnvelope(&oOldEnvelope);
1408 1 : poGeom->getEnvelope(&oNewEnvelope);
1409 : /* Optimization : we don't need to request the server */
1410 : /* if the new BBOX is inside the old BBOX as we have */
1411 : /* already all the features */
1412 1 : bReloadNeeded = !oOldEnvelope.Contains(oNewEnvelope);
1413 : }
1414 : else
1415 : {
1416 16 : bReloadNeeded = true;
1417 : }
1418 29 : nFeatures = -1;
1419 29 : const OGRErr eErr = OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
1420 29 : ResetReading();
1421 29 : return eErr;
1422 : }
1423 :
1424 : /************************************************************************/
1425 : /* SetAttributeFilter() */
1426 : /************************************************************************/
1427 :
1428 92 : OGRErr OGRWFSLayer::SetAttributeFilter(const char *pszFilter)
1429 : {
1430 92 : if (pszFilter != nullptr && pszFilter[0] == 0)
1431 8 : pszFilter = nullptr;
1432 :
1433 184 : CPLString osOldWFSWhere(osWFSWhere);
1434 :
1435 92 : CPLFree(m_pszAttrQueryString);
1436 92 : m_pszAttrQueryString = (pszFilter) ? CPLStrdup(pszFilter) : nullptr;
1437 :
1438 92 : delete m_poAttrQuery;
1439 92 : m_poAttrQuery = nullptr;
1440 :
1441 92 : if (pszFilter != nullptr)
1442 : {
1443 60 : m_poAttrQuery = new OGRFeatureQuery();
1444 :
1445 60 : OGRErr eErr = m_poAttrQuery->Compile(GetLayerDefn(), pszFilter, TRUE,
1446 : WFSGetCustomFuncRegistrar());
1447 60 : if (eErr != OGRERR_NONE)
1448 : {
1449 28 : delete m_poAttrQuery;
1450 28 : m_poAttrQuery = nullptr;
1451 28 : return eErr;
1452 : }
1453 : }
1454 :
1455 64 : if (poDS->HasMinOperators() && m_poAttrQuery != nullptr)
1456 : {
1457 30 : swq_expr_node *poNode = (swq_expr_node *)m_poAttrQuery->GetSWQExpr();
1458 30 : poNode->ReplaceBetweenByGEAndLERecurse();
1459 30 : poNode->ReplaceInByOrRecurse();
1460 :
1461 30 : int bNeedsNullCheck = FALSE;
1462 60 : int nVersion = (strcmp(poDS->GetVersion(), "1.0.0") == 0) ? 100
1463 30 : : (atoi(poDS->GetVersion()) >= 2) ? 200
1464 30 : : 110;
1465 30 : if (poNode->field_type != SWQ_BOOLEAN)
1466 0 : osWFSWhere = "";
1467 : else
1468 60 : osWFSWhere = WFS_TurnSQLFilterToOGCFilter(
1469 : poNode, nullptr, GetLayerDefn(), nVersion,
1470 30 : poDS->PropertyIsNotEqualToSupported(),
1471 30 : poDS->UseFeatureId() || bUseFeatureIdAtLayerLevel,
1472 60 : poDS->DoesGmlObjectIdNeedGMLPrefix(), "", &bNeedsNullCheck);
1473 30 : if (bNeedsNullCheck && !poDS->HasNullCheck())
1474 0 : osWFSWhere = "";
1475 : }
1476 : else
1477 34 : osWFSWhere = "";
1478 :
1479 64 : if (m_poAttrQuery != nullptr && osWFSWhere.empty())
1480 : {
1481 2 : CPLDebug("WFS", "Using client-side only mode for filter \"%s\"",
1482 : pszFilter);
1483 2 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
1484 2 : if (eErr != OGRERR_NONE)
1485 0 : return eErr;
1486 : }
1487 64 : ResetReading();
1488 :
1489 64 : osSQLWhere = (pszFilter) ? pszFilter : "";
1490 :
1491 64 : if (osWFSWhere != osOldWFSWhere)
1492 46 : bReloadNeeded = true;
1493 : else
1494 18 : bReloadNeeded = false;
1495 64 : nFeatures = -1;
1496 :
1497 64 : return OGRERR_NONE;
1498 : }
1499 :
1500 : /************************************************************************/
1501 : /* TestCapability() */
1502 : /************************************************************************/
1503 :
1504 146 : int OGRWFSLayer::TestCapability(const char *pszCap)
1505 :
1506 : {
1507 146 : if (EQUAL(pszCap, OLCFastFeatureCount))
1508 : {
1509 1 : if (nFeatures >= 0)
1510 0 : return TRUE;
1511 :
1512 1 : return poBaseLayer != nullptr && m_poFilterGeom == nullptr &&
1513 1 : m_poAttrQuery == nullptr &&
1514 2 : poBaseLayer->TestCapability(pszCap) &&
1515 0 : (!poDS->IsPagingAllowed() &&
1516 1 : poBaseLayer->GetFeatureCount() < poDS->GetPageSize());
1517 : }
1518 :
1519 145 : else if (EQUAL(pszCap, OLCFastGetExtent))
1520 : {
1521 7 : if (m_oExtents.IsInit())
1522 0 : return TRUE;
1523 :
1524 7 : return poBaseLayer != nullptr && poBaseLayer->TestCapability(pszCap);
1525 : }
1526 :
1527 138 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
1528 2 : return poBaseLayer != nullptr && poBaseLayer->TestCapability(pszCap);
1529 :
1530 136 : else if (EQUAL(pszCap, OLCSequentialWrite) ||
1531 98 : EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCRandomWrite))
1532 : {
1533 78 : GetLayerDefn();
1534 156 : return poDS->SupportTransactions() && poDS->UpdateMode() &&
1535 156 : poFeatureDefn->GetFieldIndex("gml_id") == 0;
1536 : }
1537 58 : else if (EQUAL(pszCap, OLCTransactions))
1538 : {
1539 50 : return poDS->SupportTransactions() && poDS->UpdateMode();
1540 : }
1541 8 : else if (EQUAL(pszCap, OLCIgnoreFields))
1542 : {
1543 0 : return TRUE;
1544 : }
1545 :
1546 8 : return FALSE;
1547 : }
1548 :
1549 : /************************************************************************/
1550 : /* ExecuteGetFeatureResultTypeHits() */
1551 : /************************************************************************/
1552 :
1553 9 : GIntBig OGRWFSLayer::ExecuteGetFeatureResultTypeHits()
1554 : {
1555 9 : char *pabyData = nullptr;
1556 18 : CPLString osURL = MakeGetFeatureURL(0, TRUE);
1557 9 : if (pszRequiredOutputFormat)
1558 0 : osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT",
1559 0 : WFS_EscapeURL(pszRequiredOutputFormat));
1560 9 : CPLDebug("WFS", "%s", osURL.c_str());
1561 :
1562 9 : CPLHTTPResult *psResult = poDS->HTTPFetch(osURL, nullptr);
1563 9 : if (psResult == nullptr)
1564 : {
1565 1 : return -1;
1566 : }
1567 :
1568 : /* http://demo.snowflakesoftware.com:8080/Obstacle_AIXM_ZIP/GOPublisherWFS
1569 : * returns */
1570 : /* zip content, including for RESULTTYPE=hits */
1571 8 : if (psResult->pszContentType != nullptr &&
1572 0 : strstr(psResult->pszContentType, "application/zip") != nullptr)
1573 : {
1574 : const CPLString osTmpFileName(
1575 0 : VSIMemGenerateHiddenFilename("wfstemphits.zip"));
1576 0 : VSILFILE *fp = VSIFileFromMemBuffer(osTmpFileName, psResult->pabyData,
1577 0 : psResult->nDataLen, FALSE);
1578 0 : VSIFCloseL(fp);
1579 :
1580 0 : CPLString osZipTmpFileName("/vsizip/" + osTmpFileName);
1581 :
1582 0 : char **papszDirContent = VSIReadDir(osZipTmpFileName);
1583 0 : if (CSLCount(papszDirContent) != 1)
1584 : {
1585 0 : CPLError(CE_Failure, CPLE_AppDefined,
1586 : "Cannot parse result of RESULTTYPE=hits request : more "
1587 : "than one file in zip");
1588 0 : CSLDestroy(papszDirContent);
1589 0 : CPLHTTPDestroyResult(psResult);
1590 0 : VSIUnlink(osTmpFileName);
1591 0 : return -1;
1592 : }
1593 :
1594 0 : CPLString osFileInZipTmpFileName = osZipTmpFileName + "/";
1595 0 : osFileInZipTmpFileName += papszDirContent[0];
1596 :
1597 0 : fp = VSIFOpenL(osFileInZipTmpFileName.c_str(), "rb");
1598 : VSIStatBufL sBuf;
1599 0 : if (fp == nullptr ||
1600 0 : VSIStatL(osFileInZipTmpFileName.c_str(), &sBuf) != 0)
1601 : {
1602 0 : CPLError(CE_Failure, CPLE_AppDefined,
1603 : "Cannot parse result of RESULTTYPE=hits request : cannot "
1604 : "open one file in zip");
1605 0 : CSLDestroy(papszDirContent);
1606 0 : CPLHTTPDestroyResult(psResult);
1607 0 : VSIUnlink(osTmpFileName);
1608 0 : if (fp)
1609 0 : VSIFCloseL(fp);
1610 0 : return -1;
1611 : }
1612 0 : pabyData = (char *)CPLMalloc((size_t)(sBuf.st_size + 1));
1613 0 : pabyData[sBuf.st_size] = 0;
1614 0 : VSIFReadL(pabyData, 1, (size_t)sBuf.st_size, fp);
1615 0 : VSIFCloseL(fp);
1616 :
1617 0 : CSLDestroy(papszDirContent);
1618 0 : VSIUnlink(osTmpFileName);
1619 : }
1620 : else
1621 : {
1622 8 : pabyData = (char *)psResult->pabyData;
1623 8 : psResult->pabyData = nullptr;
1624 : }
1625 :
1626 8 : if (strstr(pabyData, "<ServiceExceptionReport") != nullptr ||
1627 7 : strstr(pabyData, "<ows:ExceptionReport") != nullptr)
1628 : {
1629 1 : if (poDS->IsOldDeegree(pabyData))
1630 : {
1631 0 : CPLHTTPDestroyResult(psResult);
1632 0 : return ExecuteGetFeatureResultTypeHits();
1633 : }
1634 1 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
1635 : pabyData);
1636 1 : CPLHTTPDestroyResult(psResult);
1637 1 : CPLFree(pabyData);
1638 1 : return -1;
1639 : }
1640 :
1641 7 : CPLXMLNode *psXML = CPLParseXMLString(pabyData);
1642 7 : if (psXML == nullptr)
1643 : {
1644 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
1645 : pabyData);
1646 2 : CPLHTTPDestroyResult(psResult);
1647 2 : CPLFree(pabyData);
1648 2 : return -1;
1649 : }
1650 :
1651 5 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
1652 5 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=FeatureCollection");
1653 5 : if (psRoot == nullptr)
1654 : {
1655 1 : CPLError(CE_Failure, CPLE_AppDefined,
1656 : "Cannot find <FeatureCollection>");
1657 1 : CPLDestroyXMLNode(psXML);
1658 1 : CPLHTTPDestroyResult(psResult);
1659 1 : CPLFree(pabyData);
1660 1 : return -1;
1661 : }
1662 :
1663 4 : const char *pszValue = CPLGetXMLValue(psRoot, "numberOfFeatures", nullptr);
1664 4 : if (pszValue == nullptr)
1665 : pszValue =
1666 3 : CPLGetXMLValue(psRoot, "numberMatched", nullptr); /* WFS 2.0.0 */
1667 4 : if (pszValue == nullptr)
1668 : {
1669 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find numberOfFeatures");
1670 1 : CPLDestroyXMLNode(psXML);
1671 1 : CPLHTTPDestroyResult(psResult);
1672 1 : CPLFree(pabyData);
1673 :
1674 1 : poDS->DisableSupportHits();
1675 1 : return -1;
1676 : }
1677 :
1678 3 : GIntBig l_nFeatures = CPLAtoGIntBig(pszValue);
1679 : /* Hum,
1680 : * http://deegree3-testing.deegree.org:80/deegree-inspire-node/services?MAXFEATURES=10&SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=ad:Address&OUTPUTFORMAT=text/xml;%20subtype=gml/3.2.1&RESULTTYPE=hits
1681 : */
1682 : /* returns more than MAXFEATURES features... So truncate to MAXFEATURES */
1683 : CPLString osMaxFeatures = CPLURLGetValue(
1684 3 : osURL, atoi(poDS->GetVersion()) >= 2 ? "COUNT" : "MAXFEATURES");
1685 3 : if (!osMaxFeatures.empty())
1686 : {
1687 0 : GIntBig nMaxFeatures = CPLAtoGIntBig(osMaxFeatures);
1688 0 : if (l_nFeatures > nMaxFeatures)
1689 : {
1690 0 : CPLDebug("WFS",
1691 : "Truncating result from " CPL_FRMT_GIB " to " CPL_FRMT_GIB,
1692 : l_nFeatures, nMaxFeatures);
1693 0 : l_nFeatures = nMaxFeatures;
1694 : }
1695 : }
1696 :
1697 3 : CPLDestroyXMLNode(psXML);
1698 3 : CPLHTTPDestroyResult(psResult);
1699 3 : CPLFree(pabyData);
1700 :
1701 3 : return l_nFeatures;
1702 : }
1703 :
1704 : /************************************************************************/
1705 : /* CanRunGetFeatureCountAndGetExtentTogether() */
1706 : /************************************************************************/
1707 :
1708 16 : int OGRWFSLayer::CanRunGetFeatureCountAndGetExtentTogether()
1709 : {
1710 : /* In some cases, we can evaluate the result of GetFeatureCount() */
1711 : /* and GetExtent() with the same data */
1712 16 : CPLString osRequestURL = MakeGetFeatureURL(0, FALSE);
1713 26 : return (!m_oExtents.IsInit() && nFeatures < 0 &&
1714 10 : osRequestURL.ifind("FILTER") == std::string::npos &&
1715 10 : osRequestURL.ifind("MAXFEATURES") == std::string::npos &&
1716 34 : osRequestURL.ifind("COUNT") == std::string::npos &&
1717 40 : !(GetLayerDefn()->IsGeometryIgnored()));
1718 : }
1719 :
1720 : /************************************************************************/
1721 : /* GetFeatureCount() */
1722 : /************************************************************************/
1723 :
1724 14 : GIntBig OGRWFSLayer::GetFeatureCount(int bForce)
1725 : {
1726 14 : if (nFeatures >= 0)
1727 2 : return nFeatures;
1728 :
1729 12 : if (poBaseLayer && TestCapability(OLCFastFeatureCount))
1730 0 : return poBaseLayer->GetFeatureCount(bForce);
1731 :
1732 22 : if ((m_poAttrQuery == nullptr || !osWFSWhere.empty()) &&
1733 10 : poDS->GetFeatureSupportHits())
1734 : {
1735 9 : nFeatures = ExecuteGetFeatureResultTypeHits();
1736 9 : if (nFeatures >= 0)
1737 3 : return nFeatures;
1738 : }
1739 :
1740 : /* If we have not yet the base layer, try to read one */
1741 : /* feature, and then query again OLCFastFeatureCount on the */
1742 : /* base layer. In case the WFS response would contain the */
1743 : /* number of features */
1744 17 : if (poBaseLayer == nullptr &&
1745 8 : (m_poAttrQuery == nullptr || !osWFSWhere.empty()))
1746 : {
1747 7 : ResetReading();
1748 7 : OGRFeature *poFeature = GetNextFeature();
1749 7 : delete poFeature;
1750 7 : ResetReading();
1751 :
1752 7 : if (nFeatures >= 0)
1753 0 : return nFeatures;
1754 :
1755 7 : if (poBaseLayer && TestCapability(OLCFastFeatureCount))
1756 0 : return poBaseLayer->GetFeatureCount(bForce);
1757 : }
1758 :
1759 : /* In some cases, we can evaluate the result of GetFeatureCount() */
1760 : /* and GetExtent() with the same data */
1761 9 : if (CanRunGetFeatureCountAndGetExtentTogether())
1762 : {
1763 1 : OGREnvelope sDummy;
1764 1 : CPL_IGNORE_RET_VAL(GetExtent(&sDummy));
1765 : }
1766 :
1767 9 : if (nFeatures < 0)
1768 9 : nFeatures = OGRLayer::GetFeatureCount(bForce);
1769 :
1770 9 : return nFeatures;
1771 : }
1772 :
1773 : /************************************************************************/
1774 : /* SetExtents() */
1775 : /************************************************************************/
1776 :
1777 152 : void OGRWFSLayer::SetExtents(double dfMinXIn, double dfMinYIn, double dfMaxXIn,
1778 : double dfMaxYIn)
1779 : {
1780 152 : m_oExtents.MinX = dfMinXIn;
1781 152 : m_oExtents.MinY = dfMinYIn;
1782 152 : m_oExtents.MaxX = dfMaxXIn;
1783 152 : m_oExtents.MaxY = dfMaxYIn;
1784 152 : }
1785 :
1786 : /************************************************************************/
1787 : /* SetWGS84Extents() */
1788 : /************************************************************************/
1789 :
1790 152 : void OGRWFSLayer::SetWGS84Extents(double dfMinXIn, double dfMinYIn,
1791 : double dfMaxXIn, double dfMaxYIn)
1792 : {
1793 152 : m_oWGS84Extents.MinX = dfMinXIn;
1794 152 : m_oWGS84Extents.MinY = dfMinYIn;
1795 152 : m_oWGS84Extents.MaxX = dfMaxXIn;
1796 152 : m_oWGS84Extents.MaxY = dfMaxYIn;
1797 152 : }
1798 :
1799 : /************************************************************************/
1800 : /* IGetExtent() */
1801 : /************************************************************************/
1802 :
1803 16 : OGRErr OGRWFSLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
1804 : bool bForce)
1805 : {
1806 16 : if (m_oExtents.IsInit())
1807 : {
1808 9 : *psExtent = m_oExtents;
1809 9 : return OGRERR_NONE;
1810 : }
1811 :
1812 : /* If we have not yet the base layer, try to read one */
1813 : /* feature, and then query again OLCFastGetExtent on the */
1814 : /* base layer. In case the WFS response would contain the */
1815 : /* global extent */
1816 7 : if (poBaseLayer == nullptr)
1817 : {
1818 7 : ResetReading();
1819 7 : OGRFeature *poFeature = GetNextFeature();
1820 7 : delete poFeature;
1821 7 : ResetReading();
1822 : }
1823 :
1824 7 : if (TestCapability(OLCFastGetExtent))
1825 0 : return poBaseLayer->GetExtent(iGeomField, psExtent, bForce);
1826 :
1827 : /* In some cases, we can evaluate the result of GetFeatureCount() */
1828 : /* and GetExtent() with the same data */
1829 7 : if (CanRunGetFeatureCountAndGetExtentTogether())
1830 : {
1831 7 : bCountFeaturesInGetNextFeature = true;
1832 7 : nFeatures = 0;
1833 : }
1834 :
1835 7 : OGRErr eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1836 :
1837 7 : if (bCountFeaturesInGetNextFeature)
1838 : {
1839 7 : if (eErr == OGRERR_NONE)
1840 : {
1841 2 : m_oExtents = *psExtent;
1842 : }
1843 : else
1844 : {
1845 5 : nFeatures = -1;
1846 : }
1847 7 : bCountFeaturesInGetNextFeature = false;
1848 : }
1849 :
1850 7 : return eErr;
1851 : }
1852 :
1853 : /************************************************************************/
1854 : /* GetShortName() */
1855 : /************************************************************************/
1856 :
1857 718 : const char *OGRWFSLayer::GetShortName()
1858 : {
1859 718 : const char *pszShortName = strchr(pszName, ':');
1860 718 : if (pszShortName == nullptr)
1861 622 : pszShortName = pszName;
1862 : else
1863 96 : pszShortName++;
1864 718 : return pszShortName;
1865 : }
1866 :
1867 : /************************************************************************/
1868 : /* GetPostHeader() */
1869 : /************************************************************************/
1870 :
1871 62 : CPLString OGRWFSLayer::GetPostHeader()
1872 : {
1873 62 : CPLString osPost;
1874 62 : osPost += "<?xml version=\"1.0\"?>\n";
1875 62 : osPost += "<wfs:Transaction xmlns:wfs=\"http://www.opengis.net/wfs\"\n";
1876 : osPost += " "
1877 62 : "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
1878 62 : osPost += " service=\"WFS\" version=\"";
1879 62 : osPost += poDS->GetVersion();
1880 62 : osPost += "\"\n";
1881 62 : osPost += " xmlns:gml=\"http://www.opengis.net/gml\"\n";
1882 62 : osPost += " xmlns:ogc=\"http://www.opengis.net/ogc\"\n";
1883 : osPost +=
1884 : " xsi:schemaLocation=\"http://www.opengis.net/wfs "
1885 62 : "http://schemas.opengis.net/wfs/";
1886 62 : osPost += poDS->GetVersion();
1887 62 : osPost += "/wfs.xsd ";
1888 62 : osPost += osTargetNamespace;
1889 62 : osPost += " ";
1890 :
1891 : char *pszXMLEncoded =
1892 62 : CPLEscapeString(GetDescribeFeatureTypeURL(FALSE), -1, CPLES_XML);
1893 62 : osPost += pszXMLEncoded;
1894 62 : CPLFree(pszXMLEncoded);
1895 :
1896 62 : osPost += "\">\n";
1897 :
1898 62 : return osPost;
1899 : }
1900 :
1901 : /************************************************************************/
1902 : /* ICreateFeature() */
1903 : /************************************************************************/
1904 :
1905 38 : OGRErr OGRWFSLayer::ICreateFeature(OGRFeature *poFeature)
1906 : {
1907 38 : if (!TestCapability(OLCSequentialWrite))
1908 : {
1909 0 : if (!poDS->SupportTransactions())
1910 0 : CPLError(CE_Failure, CPLE_AppDefined,
1911 : "CreateFeature() not supported: no WMS-T features "
1912 : "advertized by server");
1913 0 : else if (!poDS->UpdateMode())
1914 0 : CPLError(CE_Failure, CPLE_AppDefined,
1915 : "CreateFeature() not supported: datasource opened as "
1916 : "read-only");
1917 0 : return OGRERR_FAILURE;
1918 : }
1919 :
1920 38 : if (poGMLFeatureClass == nullptr)
1921 : {
1922 0 : CPLError(CE_Failure, CPLE_AppDefined,
1923 : "Cannot insert feature because we didn't manage to parse the "
1924 : ".XSD schema");
1925 0 : return OGRERR_FAILURE;
1926 : }
1927 :
1928 38 : if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
1929 : {
1930 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
1931 0 : return OGRERR_FAILURE;
1932 : }
1933 :
1934 38 : if (poFeature->IsFieldSetAndNotNull(0))
1935 : {
1936 2 : CPLError(CE_Failure, CPLE_AppDefined,
1937 : "Cannot insert a feature when gml_id field is already set");
1938 2 : return OGRERR_FAILURE;
1939 : }
1940 :
1941 72 : CPLString osPost;
1942 :
1943 36 : const char *pszShortName = GetShortName();
1944 :
1945 36 : if (!bInTransaction)
1946 : {
1947 20 : osPost += GetPostHeader();
1948 20 : osPost += " <wfs:Insert>\n";
1949 : }
1950 36 : osPost += " <feature:";
1951 36 : osPost += pszShortName;
1952 36 : osPost += " xmlns:feature=\"";
1953 36 : osPost += osTargetNamespace;
1954 36 : osPost += "\">\n";
1955 :
1956 288 : for (int i = 1; i <= poFeature->GetFieldCount(); i++)
1957 : {
1958 576 : if (poGMLFeatureClass->GetGeometryPropertyCount() == 1 &&
1959 288 : poGMLFeatureClass->GetGeometryProperty(0)->GetAttributeIndex() ==
1960 288 : i - 1)
1961 : {
1962 36 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1963 36 : if (poGeom != nullptr && !osGeometryColumnName.empty())
1964 : {
1965 2 : if (poGeom->getSpatialReference() == nullptr)
1966 2 : poGeom->assignSpatialReference(m_poSRS);
1967 2 : char *pszGML = nullptr;
1968 2 : if (strcmp(poDS->GetVersion(), "1.1.0") == 0 ||
1969 0 : atoi(poDS->GetVersion()) >= 2)
1970 : {
1971 2 : char **papszOptions = CSLAddString(nullptr, "FORMAT=GML3");
1972 : pszGML =
1973 2 : OGR_G_ExportToGMLEx((OGRGeometryH)poGeom, papszOptions);
1974 2 : CSLDestroy(papszOptions);
1975 : }
1976 : else
1977 0 : pszGML = OGR_G_ExportToGML((OGRGeometryH)poGeom);
1978 2 : osPost += " <feature:";
1979 2 : osPost += osGeometryColumnName;
1980 2 : osPost += ">";
1981 2 : osPost += pszGML;
1982 2 : osPost += "</feature:";
1983 2 : osPost += osGeometryColumnName;
1984 2 : osPost += ">\n";
1985 2 : CPLFree(pszGML);
1986 : }
1987 : }
1988 288 : if (i == poFeature->GetFieldCount())
1989 36 : break;
1990 :
1991 : #ifdef notdef
1992 : if (poFeature->IsFieldNull(i))
1993 : {
1994 : OGRFieldDefn *poFDefn = poFeature->GetFieldDefnRef(i);
1995 : osPost += " <feature:";
1996 : osPost += poFDefn->GetNameRef();
1997 : osPost += " xsi:nil=\"true\" />\n";
1998 : }
1999 : else
2000 : #endif
2001 252 : if (poFeature->IsFieldSet(i) && !poFeature->IsFieldNull(i))
2002 : {
2003 6 : OGRFieldDefn *poFDefn = poFeature->GetFieldDefnRef(i);
2004 6 : osPost += " <feature:";
2005 6 : osPost += poFDefn->GetNameRef();
2006 6 : osPost += ">";
2007 6 : if (poFDefn->GetType() == OFTInteger)
2008 2 : osPost += CPLSPrintf("%d", poFeature->GetFieldAsInteger(i));
2009 4 : else if (poFDefn->GetType() == OFTInteger64)
2010 : osPost +=
2011 0 : CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFieldAsInteger64(i));
2012 4 : else if (poFDefn->GetType() == OFTReal)
2013 2 : osPost += CPLSPrintf("%.16g", poFeature->GetFieldAsDouble(i));
2014 : else
2015 : {
2016 2 : char *pszXMLEncoded = CPLEscapeString(
2017 : poFeature->GetFieldAsString(i), -1, CPLES_XML);
2018 2 : osPost += pszXMLEncoded;
2019 2 : CPLFree(pszXMLEncoded);
2020 : }
2021 6 : osPost += "</feature:";
2022 6 : osPost += poFDefn->GetNameRef();
2023 6 : osPost += ">\n";
2024 : }
2025 : }
2026 :
2027 36 : osPost += " </feature:";
2028 36 : osPost += pszShortName;
2029 36 : osPost += ">\n";
2030 :
2031 36 : if (!bInTransaction)
2032 : {
2033 20 : osPost += " </wfs:Insert>\n";
2034 20 : osPost += "</wfs:Transaction>\n";
2035 : }
2036 : else
2037 : {
2038 16 : osGlobalInsert += osPost;
2039 16 : nExpectedInserts++;
2040 16 : return OGRERR_NONE;
2041 : }
2042 :
2043 20 : CPLDebug("WFS", "Post : %s", osPost.c_str());
2044 :
2045 20 : char **papszOptions = nullptr;
2046 20 : papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
2047 : papszOptions =
2048 20 : CSLAddNameValue(papszOptions, "HEADERS",
2049 : "Content-Type: application/xml; charset=UTF-8");
2050 :
2051 : CPLHTTPResult *psResult =
2052 20 : poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
2053 20 : CSLDestroy(papszOptions);
2054 :
2055 20 : if (psResult == nullptr)
2056 : {
2057 6 : return OGRERR_FAILURE;
2058 : }
2059 :
2060 14 : if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
2061 12 : nullptr ||
2062 12 : strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
2063 : nullptr)
2064 : {
2065 2 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
2066 : psResult->pabyData);
2067 2 : CPLHTTPDestroyResult(psResult);
2068 2 : return OGRERR_FAILURE;
2069 : }
2070 :
2071 12 : CPLDebug("WFS", "Response: %s", psResult->pabyData);
2072 :
2073 12 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
2074 12 : if (psXML == nullptr)
2075 : {
2076 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
2077 : psResult->pabyData);
2078 2 : CPLHTTPDestroyResult(psResult);
2079 2 : return OGRERR_FAILURE;
2080 : }
2081 :
2082 10 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
2083 10 : bool bUse100Schema = false;
2084 10 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
2085 10 : if (psRoot == nullptr)
2086 : {
2087 2 : psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
2088 2 : if (psRoot)
2089 0 : bUse100Schema = true;
2090 : }
2091 :
2092 10 : if (psRoot == nullptr)
2093 : {
2094 2 : CPLError(CE_Failure, CPLE_AppDefined,
2095 : "Cannot find <TransactionResponse>");
2096 2 : CPLDestroyXMLNode(psXML);
2097 2 : CPLHTTPDestroyResult(psResult);
2098 2 : return OGRERR_FAILURE;
2099 : }
2100 :
2101 8 : CPLXMLNode *psFeatureID = nullptr;
2102 :
2103 8 : if (bUse100Schema)
2104 : {
2105 0 : if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
2106 : {
2107 0 : CPLError(CE_Failure, CPLE_AppDefined, "Insert failed : %s",
2108 : psResult->pabyData);
2109 0 : CPLDestroyXMLNode(psXML);
2110 0 : CPLHTTPDestroyResult(psResult);
2111 0 : return OGRERR_FAILURE;
2112 : }
2113 :
2114 0 : psFeatureID = CPLGetXMLNode(psRoot, "InsertResult.FeatureId");
2115 0 : if (psFeatureID == nullptr)
2116 : {
2117 0 : CPLError(CE_Failure, CPLE_AppDefined,
2118 : "Cannot find InsertResult.FeatureId");
2119 0 : CPLDestroyXMLNode(psXML);
2120 0 : CPLHTTPDestroyResult(psResult);
2121 0 : return OGRERR_FAILURE;
2122 : }
2123 : }
2124 : else
2125 : {
2126 8 : const char *pszFeatureIdElt = atoi(poDS->GetVersion()) >= 2
2127 8 : ? "InsertResults.Feature.ResourceId"
2128 8 : : "InsertResults.Feature.FeatureId";
2129 8 : psFeatureID = CPLGetXMLNode(psRoot, pszFeatureIdElt);
2130 8 : if (psFeatureID == nullptr)
2131 : {
2132 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
2133 : pszFeatureIdElt);
2134 2 : CPLDestroyXMLNode(psXML);
2135 2 : CPLHTTPDestroyResult(psResult);
2136 2 : return OGRERR_FAILURE;
2137 : }
2138 : }
2139 :
2140 6 : const char *pszFIDAttr = atoi(poDS->GetVersion()) >= 2 ? "rid" : "fid";
2141 6 : const char *pszFID = CPLGetXMLValue(psFeatureID, pszFIDAttr, nullptr);
2142 6 : if (pszFID == nullptr)
2143 : {
2144 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", pszFIDAttr);
2145 2 : CPLDestroyXMLNode(psXML);
2146 2 : CPLHTTPDestroyResult(psResult);
2147 2 : return OGRERR_FAILURE;
2148 : }
2149 :
2150 4 : poFeature->SetField("gml_id", pszFID);
2151 :
2152 : /* If the returned fid is of the form layer_name.num, then use */
2153 : /* num as the OGR FID */
2154 4 : if (strncmp(pszFID, pszShortName, strlen(pszShortName)) == 0 &&
2155 4 : pszFID[strlen(pszShortName)] == '.')
2156 : {
2157 4 : GIntBig nFID = CPLAtoGIntBig(pszFID + strlen(pszShortName) + 1);
2158 4 : poFeature->SetFID(nFID);
2159 : }
2160 :
2161 4 : CPLDebug("WFS", "Got FID = " CPL_FRMT_GIB, poFeature->GetFID());
2162 :
2163 4 : CPLDestroyXMLNode(psXML);
2164 4 : CPLHTTPDestroyResult(psResult);
2165 :
2166 : /* Invalidate layer */
2167 4 : bReloadNeeded = true;
2168 4 : nFeatures = -1;
2169 4 : m_oExtents = OGREnvelope();
2170 :
2171 4 : return OGRERR_NONE;
2172 : }
2173 :
2174 : /************************************************************************/
2175 : /* ISetFeature() */
2176 : /************************************************************************/
2177 :
2178 16 : OGRErr OGRWFSLayer::ISetFeature(OGRFeature *poFeature)
2179 : {
2180 16 : if (!TestCapability(OLCRandomWrite))
2181 : {
2182 0 : if (!poDS->SupportTransactions())
2183 0 : CPLError(CE_Failure, CPLE_AppDefined,
2184 : "SetFeature() not supported: no WMS-T features advertized "
2185 : "by server");
2186 0 : else if (!poDS->UpdateMode())
2187 0 : CPLError(
2188 : CE_Failure, CPLE_AppDefined,
2189 : "SetFeature() not supported: datasource opened as read-only");
2190 0 : return OGRERR_FAILURE;
2191 : }
2192 :
2193 16 : if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
2194 : {
2195 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
2196 0 : return OGRERR_FAILURE;
2197 : }
2198 :
2199 16 : if (poFeature->IsFieldSetAndNotNull(0) == FALSE)
2200 : {
2201 2 : CPLError(CE_Failure, CPLE_AppDefined,
2202 : "Cannot update a feature when gml_id field is not set");
2203 2 : return OGRERR_FAILURE;
2204 : }
2205 :
2206 14 : if (bInTransaction)
2207 : {
2208 0 : CPLError(
2209 : CE_Warning, CPLE_AppDefined,
2210 : "SetFeature() not yet dealt in transaction. Issued immediately");
2211 : }
2212 :
2213 14 : const char *pszShortName = GetShortName();
2214 :
2215 28 : CPLString osPost;
2216 14 : osPost += GetPostHeader();
2217 :
2218 14 : osPost += " <wfs:Update typeName=\"feature:";
2219 14 : osPost += pszShortName;
2220 14 : osPost += "\" xmlns:feature=\"";
2221 14 : osPost += osTargetNamespace;
2222 14 : osPost += "\">\n";
2223 :
2224 14 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
2225 14 : if (!osGeometryColumnName.empty())
2226 : {
2227 14 : osPost += " <wfs:Property>\n";
2228 14 : osPost += " <wfs:Name>";
2229 14 : osPost += osGeometryColumnName;
2230 14 : osPost += "</wfs:Name>\n";
2231 14 : if (poGeom != nullptr)
2232 : {
2233 2 : if (poGeom->getSpatialReference() == nullptr)
2234 2 : poGeom->assignSpatialReference(m_poSRS);
2235 2 : char *pszGML = nullptr;
2236 2 : if (strcmp(poDS->GetVersion(), "1.1.0") == 0 ||
2237 0 : atoi(poDS->GetVersion()) >= 2)
2238 : {
2239 2 : char **papszOptions = CSLAddString(nullptr, "FORMAT=GML3");
2240 : pszGML =
2241 2 : OGR_G_ExportToGMLEx((OGRGeometryH)poGeom, papszOptions);
2242 2 : CSLDestroy(papszOptions);
2243 : }
2244 : else
2245 0 : pszGML = OGR_G_ExportToGML((OGRGeometryH)poGeom);
2246 2 : osPost += " <wfs:Value>";
2247 2 : osPost += pszGML;
2248 2 : osPost += "</wfs:Value>\n";
2249 2 : CPLFree(pszGML);
2250 : }
2251 14 : osPost += " </wfs:Property>\n";
2252 : }
2253 :
2254 112 : for (int i = 1; i < poFeature->GetFieldCount(); i++)
2255 : {
2256 98 : OGRFieldDefn *poFDefn = poFeature->GetFieldDefnRef(i);
2257 :
2258 98 : osPost += " <wfs:Property>\n";
2259 98 : osPost += " <wfs:Name>";
2260 98 : osPost += poFDefn->GetNameRef();
2261 98 : osPost += "</wfs:Name>\n";
2262 98 : if (poFeature->IsFieldSetAndNotNull(i))
2263 : {
2264 6 : osPost += " <wfs:Value>";
2265 6 : if (poFDefn->GetType() == OFTInteger)
2266 2 : osPost += CPLSPrintf("%d", poFeature->GetFieldAsInteger(i));
2267 4 : else if (poFDefn->GetType() == OFTInteger64)
2268 : osPost +=
2269 0 : CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFieldAsInteger64(i));
2270 4 : else if (poFDefn->GetType() == OFTReal)
2271 2 : osPost += CPLSPrintf("%.16g", poFeature->GetFieldAsDouble(i));
2272 : else
2273 : {
2274 2 : char *pszXMLEncoded = CPLEscapeString(
2275 : poFeature->GetFieldAsString(i), -1, CPLES_XML);
2276 2 : osPost += pszXMLEncoded;
2277 2 : CPLFree(pszXMLEncoded);
2278 : }
2279 6 : osPost += "</wfs:Value>\n";
2280 : }
2281 98 : osPost += " </wfs:Property>\n";
2282 : }
2283 14 : osPost += " <ogc:Filter>\n";
2284 14 : if (poDS->UseFeatureId() || bUseFeatureIdAtLayerLevel)
2285 0 : osPost += " <ogc:FeatureId fid=\"";
2286 14 : else if (atoi(poDS->GetVersion()) >= 2)
2287 0 : osPost += " <ogc:ResourceId rid=\"";
2288 : else
2289 14 : osPost += " <ogc:GmlObjectId gml:id=\"";
2290 14 : osPost += poFeature->GetFieldAsString(0);
2291 14 : osPost += "\"/>\n";
2292 14 : osPost += " </ogc:Filter>\n";
2293 14 : osPost += " </wfs:Update>\n";
2294 14 : osPost += "</wfs:Transaction>\n";
2295 :
2296 14 : CPLDebug("WFS", "Post : %s", osPost.c_str());
2297 :
2298 14 : char **papszOptions = nullptr;
2299 14 : papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
2300 : papszOptions =
2301 14 : CSLAddNameValue(papszOptions, "HEADERS",
2302 : "Content-Type: application/xml; charset=UTF-8");
2303 :
2304 : CPLHTTPResult *psResult =
2305 14 : poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
2306 14 : CSLDestroy(papszOptions);
2307 :
2308 14 : if (psResult == nullptr)
2309 : {
2310 4 : return OGRERR_FAILURE;
2311 : }
2312 :
2313 10 : if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
2314 8 : nullptr ||
2315 8 : strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
2316 : nullptr)
2317 : {
2318 2 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
2319 : psResult->pabyData);
2320 2 : CPLHTTPDestroyResult(psResult);
2321 2 : return OGRERR_FAILURE;
2322 : }
2323 :
2324 8 : CPLDebug("WFS", "Response: %s", psResult->pabyData);
2325 :
2326 8 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
2327 8 : if (psXML == nullptr)
2328 : {
2329 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
2330 : psResult->pabyData);
2331 2 : CPLHTTPDestroyResult(psResult);
2332 2 : return OGRERR_FAILURE;
2333 : }
2334 :
2335 6 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
2336 6 : int bUse100Schema = false;
2337 6 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
2338 6 : if (psRoot == nullptr)
2339 : {
2340 2 : psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
2341 2 : if (psRoot)
2342 0 : bUse100Schema = true;
2343 : }
2344 6 : if (psRoot == nullptr)
2345 : {
2346 2 : CPLError(CE_Failure, CPLE_AppDefined,
2347 : "Cannot find <TransactionResponse>");
2348 2 : CPLDestroyXMLNode(psXML);
2349 2 : CPLHTTPDestroyResult(psResult);
2350 2 : return OGRERR_FAILURE;
2351 : }
2352 :
2353 4 : if (bUse100Schema)
2354 : {
2355 0 : if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
2356 : {
2357 0 : CPLError(CE_Failure, CPLE_AppDefined, "Update failed : %s",
2358 : psResult->pabyData);
2359 0 : CPLDestroyXMLNode(psXML);
2360 0 : CPLHTTPDestroyResult(psResult);
2361 0 : return OGRERR_FAILURE;
2362 : }
2363 : }
2364 :
2365 4 : CPLDestroyXMLNode(psXML);
2366 4 : CPLHTTPDestroyResult(psResult);
2367 :
2368 : /* Invalidate layer */
2369 4 : bReloadNeeded = true;
2370 4 : nFeatures = -1;
2371 4 : m_oExtents = OGREnvelope();
2372 :
2373 4 : return OGRERR_NONE;
2374 : }
2375 :
2376 : /************************************************************************/
2377 : /* GetFeature() */
2378 : /************************************************************************/
2379 :
2380 14 : OGRFeature *OGRWFSLayer::GetFeature(GIntBig nFID)
2381 : {
2382 14 : GetLayerDefn();
2383 14 : if (poBaseLayer == nullptr && poFeatureDefn->GetFieldIndex("gml_id") == 0)
2384 : {
2385 : /* This is lovely hackish. We assume that then gml_id will be */
2386 : /* layer_name.number. This is actually what we can observe with */
2387 : /* GeoServer and TinyOWS */
2388 : CPLString osVal =
2389 14 : CPLSPrintf("gml_id = '%s." CPL_FRMT_GIB "'", GetShortName(), nFID);
2390 14 : CPLString osOldSQLWhere(osSQLWhere);
2391 14 : SetAttributeFilter(osVal);
2392 14 : OGRFeature *poFeature = GetNextFeature();
2393 : const char *pszOldFilter =
2394 14 : osOldSQLWhere.size() ? osOldSQLWhere.c_str() : nullptr;
2395 14 : SetAttributeFilter(pszOldFilter);
2396 14 : if (poFeature)
2397 12 : return poFeature;
2398 : }
2399 :
2400 2 : return OGRLayer::GetFeature(nFID);
2401 : }
2402 :
2403 : /************************************************************************/
2404 : /* DeleteFromFilter() */
2405 : /************************************************************************/
2406 :
2407 12 : OGRErr OGRWFSLayer::DeleteFromFilter(const std::string &osOGCFilter)
2408 : {
2409 12 : if (!TestCapability(OLCDeleteFeature))
2410 : {
2411 0 : if (!poDS->SupportTransactions())
2412 0 : CPLError(CE_Failure, CPLE_AppDefined,
2413 : "DeleteFromFilter() not supported: no WMS-T features "
2414 : "advertized by server");
2415 0 : else if (!poDS->UpdateMode())
2416 0 : CPLError(CE_Failure, CPLE_AppDefined,
2417 : "DeleteFromFilter() not supported: datasource opened as "
2418 : "read-only");
2419 0 : return OGRERR_FAILURE;
2420 : }
2421 :
2422 12 : if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
2423 : {
2424 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
2425 0 : return OGRERR_FAILURE;
2426 : }
2427 12 : const char *pszShortName = GetShortName();
2428 :
2429 24 : CPLString osPost;
2430 12 : osPost += GetPostHeader();
2431 :
2432 12 : osPost += " <wfs:Delete xmlns:feature=\"";
2433 12 : osPost += osTargetNamespace;
2434 12 : osPost += "\" typeName=\"feature:";
2435 12 : osPost += pszShortName;
2436 12 : osPost += "\">\n";
2437 12 : osPost += " <ogc:Filter>\n";
2438 12 : osPost += osOGCFilter;
2439 12 : osPost += " </ogc:Filter>\n";
2440 12 : osPost += " </wfs:Delete>\n";
2441 12 : osPost += "</wfs:Transaction>\n";
2442 :
2443 12 : CPLDebug("WFS", "Post : %s", osPost.c_str());
2444 :
2445 12 : char **papszOptions = nullptr;
2446 12 : papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
2447 : papszOptions =
2448 12 : CSLAddNameValue(papszOptions, "HEADERS",
2449 : "Content-Type: application/xml; charset=UTF-8");
2450 :
2451 : CPLHTTPResult *psResult =
2452 12 : poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
2453 12 : CSLDestroy(papszOptions);
2454 :
2455 12 : if (psResult == nullptr)
2456 : {
2457 4 : return OGRERR_FAILURE;
2458 : }
2459 :
2460 8 : if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
2461 8 : nullptr ||
2462 8 : strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
2463 : nullptr)
2464 : {
2465 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
2466 : psResult->pabyData);
2467 0 : CPLHTTPDestroyResult(psResult);
2468 0 : return OGRERR_FAILURE;
2469 : }
2470 :
2471 8 : CPLDebug("WFS", "Response: %s", psResult->pabyData);
2472 :
2473 8 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
2474 8 : if (psXML == nullptr)
2475 : {
2476 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
2477 : psResult->pabyData);
2478 2 : CPLHTTPDestroyResult(psResult);
2479 2 : return OGRERR_FAILURE;
2480 : }
2481 :
2482 6 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
2483 6 : bool bUse100Schema = false;
2484 6 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
2485 6 : if (psRoot == nullptr)
2486 : {
2487 2 : psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
2488 2 : if (psRoot)
2489 0 : bUse100Schema = true;
2490 : }
2491 6 : if (psRoot == nullptr)
2492 : {
2493 2 : CPLError(CE_Failure, CPLE_AppDefined,
2494 : "Cannot find <TransactionResponse>");
2495 2 : CPLDestroyXMLNode(psXML);
2496 2 : CPLHTTPDestroyResult(psResult);
2497 2 : return OGRERR_FAILURE;
2498 : }
2499 :
2500 4 : if (bUse100Schema)
2501 : {
2502 0 : if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
2503 : {
2504 0 : CPLError(CE_Failure, CPLE_AppDefined, "Delete failed : %s",
2505 : psResult->pabyData);
2506 0 : CPLDestroyXMLNode(psXML);
2507 0 : CPLHTTPDestroyResult(psResult);
2508 0 : return OGRERR_FAILURE;
2509 : }
2510 : }
2511 :
2512 4 : CPLDestroyXMLNode(psXML);
2513 4 : CPLHTTPDestroyResult(psResult);
2514 :
2515 : /* Invalidate layer */
2516 4 : bReloadNeeded = true;
2517 4 : nFeatures = -1;
2518 4 : m_oExtents = OGREnvelope();
2519 :
2520 4 : return OGRERR_NONE;
2521 : }
2522 :
2523 : /************************************************************************/
2524 : /* DeleteFeature() */
2525 : /************************************************************************/
2526 :
2527 12 : OGRErr OGRWFSLayer::DeleteFeature(GIntBig nFID)
2528 : {
2529 12 : if (!TestCapability(OLCDeleteFeature))
2530 : {
2531 0 : if (!poDS->SupportTransactions())
2532 0 : CPLError(CE_Failure, CPLE_AppDefined,
2533 : "DeleteFeature() not supported: no WMS-T features "
2534 : "advertized by server");
2535 0 : else if (!poDS->UpdateMode())
2536 0 : CPLError(CE_Failure, CPLE_AppDefined,
2537 : "DeleteFeature() not supported: datasource opened as "
2538 : "read-only");
2539 0 : return OGRERR_FAILURE;
2540 : }
2541 :
2542 12 : if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
2543 : {
2544 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
2545 0 : return OGRERR_FAILURE;
2546 : }
2547 :
2548 12 : OGRFeature *poFeature = GetFeature(nFID);
2549 12 : if (poFeature == nullptr)
2550 : {
2551 2 : CPLError(CE_Failure, CPLE_AppDefined,
2552 : "Cannot find feature " CPL_FRMT_GIB, nFID);
2553 2 : return OGRERR_FAILURE;
2554 : }
2555 :
2556 10 : const char *pszGMLID = poFeature->GetFieldAsString("gml_id");
2557 10 : if (pszGMLID == nullptr)
2558 : {
2559 0 : CPLError(CE_Failure, CPLE_AppDefined,
2560 : "Cannot delete a feature with gml_id unset");
2561 0 : delete poFeature;
2562 0 : return OGRERR_FAILURE;
2563 : }
2564 :
2565 10 : if (bInTransaction)
2566 : {
2567 0 : CPLError(
2568 : CE_Warning, CPLE_AppDefined,
2569 : "DeleteFeature() not yet dealt in transaction. Issued immediately");
2570 : }
2571 :
2572 20 : CPLString osGMLID = pszGMLID;
2573 10 : pszGMLID = nullptr;
2574 10 : delete poFeature;
2575 10 : poFeature = nullptr;
2576 :
2577 20 : CPLString osFilter;
2578 10 : osFilter = "<ogc:FeatureId fid=\"";
2579 10 : osFilter += osGMLID;
2580 10 : osFilter += "\"/>\n";
2581 10 : return DeleteFromFilter(osFilter);
2582 : }
2583 :
2584 : /************************************************************************/
2585 : /* StartTransaction() */
2586 : /************************************************************************/
2587 :
2588 24 : OGRErr OGRWFSLayer::StartTransaction()
2589 : {
2590 24 : if (!TestCapability(OLCTransactions))
2591 : {
2592 0 : if (!poDS->SupportTransactions())
2593 0 : CPLError(CE_Failure, CPLE_AppDefined,
2594 : "StartTransaction() not supported: no WMS-T features "
2595 : "advertized by server");
2596 0 : else if (!poDS->UpdateMode())
2597 0 : CPLError(CE_Failure, CPLE_AppDefined,
2598 : "StartTransaction() not supported: datasource opened as "
2599 : "read-only");
2600 0 : return OGRERR_FAILURE;
2601 : }
2602 :
2603 24 : if (bInTransaction)
2604 : {
2605 2 : CPLError(CE_Failure, CPLE_AppDefined,
2606 : "StartTransaction() has already been called");
2607 2 : return OGRERR_FAILURE;
2608 : }
2609 :
2610 22 : bInTransaction = true;
2611 22 : osGlobalInsert = "";
2612 22 : nExpectedInserts = 0;
2613 22 : aosFIDList.resize(0);
2614 :
2615 22 : return OGRERR_NONE;
2616 : }
2617 :
2618 : /************************************************************************/
2619 : /* CommitTransaction() */
2620 : /************************************************************************/
2621 :
2622 20 : OGRErr OGRWFSLayer::CommitTransaction()
2623 : {
2624 20 : if (!TestCapability(OLCTransactions))
2625 : {
2626 0 : if (!poDS->SupportTransactions())
2627 0 : CPLError(CE_Failure, CPLE_AppDefined,
2628 : "CommitTransaction() not supported: no WMS-T features "
2629 : "advertized by server");
2630 0 : else if (!poDS->UpdateMode())
2631 0 : CPLError(CE_Failure, CPLE_AppDefined,
2632 : "CommitTransaction() not supported: datasource opened as "
2633 : "read-only");
2634 0 : return OGRERR_FAILURE;
2635 : }
2636 :
2637 20 : if (!bInTransaction)
2638 : {
2639 2 : CPLError(CE_Failure, CPLE_AppDefined,
2640 : "StartTransaction() has not yet been called");
2641 2 : return OGRERR_FAILURE;
2642 : }
2643 :
2644 18 : if (!osGlobalInsert.empty())
2645 : {
2646 16 : CPLString osPost = GetPostHeader();
2647 16 : osPost += " <wfs:Insert>\n";
2648 16 : osPost += osGlobalInsert;
2649 16 : osPost += " </wfs:Insert>\n";
2650 16 : osPost += "</wfs:Transaction>\n";
2651 :
2652 16 : bInTransaction = false;
2653 16 : osGlobalInsert = "";
2654 16 : int l_nExpectedInserts = nExpectedInserts;
2655 16 : nExpectedInserts = 0;
2656 :
2657 16 : CPLDebug("WFS", "Post : %s", osPost.c_str());
2658 :
2659 16 : char **papszOptions = nullptr;
2660 : papszOptions =
2661 16 : CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
2662 : papszOptions =
2663 16 : CSLAddNameValue(papszOptions, "HEADERS",
2664 : "Content-Type: application/xml; charset=UTF-8");
2665 :
2666 : CPLHTTPResult *psResult =
2667 16 : poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
2668 16 : CSLDestroy(papszOptions);
2669 :
2670 16 : if (psResult == nullptr)
2671 : {
2672 0 : return OGRERR_FAILURE;
2673 : }
2674 :
2675 16 : if (strstr((const char *)psResult->pabyData,
2676 14 : "<ServiceExceptionReport") != nullptr ||
2677 14 : strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
2678 : nullptr)
2679 : {
2680 2 : CPLError(CE_Failure, CPLE_AppDefined,
2681 : "Error returned by server : %s", psResult->pabyData);
2682 2 : CPLHTTPDestroyResult(psResult);
2683 2 : return OGRERR_FAILURE;
2684 : }
2685 :
2686 14 : CPLDebug("WFS", "Response: %s", psResult->pabyData);
2687 :
2688 14 : CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
2689 14 : if (psXML == nullptr)
2690 : {
2691 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
2692 : psResult->pabyData);
2693 2 : CPLHTTPDestroyResult(psResult);
2694 2 : return OGRERR_FAILURE;
2695 : }
2696 :
2697 12 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
2698 12 : bool bUse100Schema = false;
2699 12 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
2700 12 : if (psRoot == nullptr)
2701 : {
2702 2 : psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
2703 2 : if (psRoot)
2704 0 : bUse100Schema = true;
2705 : }
2706 :
2707 12 : if (psRoot == nullptr)
2708 : {
2709 2 : CPLError(CE_Failure, CPLE_AppDefined,
2710 : "Cannot find <TransactionResponse>");
2711 2 : CPLDestroyXMLNode(psXML);
2712 2 : CPLHTTPDestroyResult(psResult);
2713 2 : return OGRERR_FAILURE;
2714 : }
2715 :
2716 10 : if (bUse100Schema)
2717 : {
2718 0 : if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
2719 : {
2720 0 : CPLError(CE_Failure, CPLE_AppDefined, "Insert failed : %s",
2721 : psResult->pabyData);
2722 0 : CPLDestroyXMLNode(psXML);
2723 0 : CPLHTTPDestroyResult(psResult);
2724 0 : return OGRERR_FAILURE;
2725 : }
2726 :
2727 : /* TODO */
2728 : }
2729 : else
2730 : {
2731 10 : int nGotInserted = atoi(
2732 : CPLGetXMLValue(psRoot, "TransactionSummary.totalInserted", ""));
2733 10 : if (nGotInserted != l_nExpectedInserts)
2734 : {
2735 2 : CPLError(
2736 : CE_Failure, CPLE_AppDefined,
2737 : "Only %d features were inserted whereas %d where expected",
2738 : nGotInserted, l_nExpectedInserts);
2739 2 : CPLDestroyXMLNode(psXML);
2740 2 : CPLHTTPDestroyResult(psResult);
2741 2 : return OGRERR_FAILURE;
2742 : }
2743 :
2744 : CPLXMLNode *psInsertResults =
2745 8 : CPLGetXMLNode(psRoot, "InsertResults");
2746 8 : if (psInsertResults == nullptr)
2747 : {
2748 2 : CPLError(CE_Failure, CPLE_AppDefined,
2749 : "Cannot find node InsertResults");
2750 2 : CPLDestroyXMLNode(psXML);
2751 2 : CPLHTTPDestroyResult(psResult);
2752 2 : return OGRERR_FAILURE;
2753 : }
2754 :
2755 6 : aosFIDList.resize(0);
2756 :
2757 6 : CPLXMLNode *psChild = psInsertResults->psChild;
2758 8 : while (psChild)
2759 : {
2760 : const char *pszFID =
2761 4 : CPLGetXMLValue(psChild, "FeatureId.fid", nullptr);
2762 4 : if (pszFID == nullptr)
2763 : {
2764 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find fid");
2765 2 : CPLDestroyXMLNode(psXML);
2766 2 : CPLHTTPDestroyResult(psResult);
2767 2 : return OGRERR_FAILURE;
2768 : }
2769 2 : aosFIDList.push_back(pszFID);
2770 :
2771 2 : psChild = psChild->psNext;
2772 : }
2773 :
2774 4 : if ((int)aosFIDList.size() != nGotInserted)
2775 : {
2776 2 : CPLError(CE_Failure, CPLE_AppDefined,
2777 : "Inconsistent InsertResults: did not get expected FID "
2778 : "count");
2779 2 : CPLDestroyXMLNode(psXML);
2780 2 : CPLHTTPDestroyResult(psResult);
2781 2 : return OGRERR_FAILURE;
2782 : }
2783 : }
2784 :
2785 2 : CPLDestroyXMLNode(psXML);
2786 2 : CPLHTTPDestroyResult(psResult);
2787 : }
2788 :
2789 4 : bInTransaction = false;
2790 4 : osGlobalInsert = "";
2791 4 : nExpectedInserts = 0;
2792 :
2793 4 : return OGRERR_NONE;
2794 : }
2795 :
2796 : /************************************************************************/
2797 : /* RollbackTransaction() */
2798 : /************************************************************************/
2799 :
2800 6 : OGRErr OGRWFSLayer::RollbackTransaction()
2801 : {
2802 6 : if (!TestCapability(OLCTransactions))
2803 : {
2804 0 : if (!poDS->SupportTransactions())
2805 0 : CPLError(CE_Failure, CPLE_AppDefined,
2806 : "RollbackTransaction() not supported: no WMS-T features "
2807 : "advertized by server");
2808 0 : else if (!poDS->UpdateMode())
2809 0 : CPLError(CE_Failure, CPLE_AppDefined,
2810 : "RollbackTransaction() not supported: datasource opened "
2811 : "as read-only");
2812 0 : return OGRERR_FAILURE;
2813 : }
2814 :
2815 6 : if (!bInTransaction)
2816 : {
2817 2 : CPLError(CE_Failure, CPLE_AppDefined,
2818 : "StartTransaction() has not yet been called");
2819 2 : return OGRERR_FAILURE;
2820 : }
2821 :
2822 4 : bInTransaction = false;
2823 4 : osGlobalInsert = "";
2824 4 : nExpectedInserts = 0;
2825 :
2826 4 : return OGRERR_NONE;
2827 : }
2828 :
2829 : /************************************************************************/
2830 : /* SetRequiredOutputFormat() */
2831 : /************************************************************************/
2832 :
2833 0 : void OGRWFSLayer::SetRequiredOutputFormat(const char *pszRequiredOutputFormatIn)
2834 : {
2835 0 : CPLFree(pszRequiredOutputFormat);
2836 0 : if (pszRequiredOutputFormatIn)
2837 : {
2838 0 : pszRequiredOutputFormat = CPLStrdup(pszRequiredOutputFormatIn);
2839 : }
2840 : else
2841 : {
2842 0 : pszRequiredOutputFormat = nullptr;
2843 : }
2844 0 : }
2845 :
2846 : /************************************************************************/
2847 : /* SetOrderBy() */
2848 : /************************************************************************/
2849 :
2850 0 : void OGRWFSLayer::SetOrderBy(const std::vector<OGRWFSSortDesc> &aoSortColumnsIn)
2851 : {
2852 0 : aoSortColumns = aoSortColumnsIn;
2853 0 : }
|