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