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