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