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