LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/wfs - ogrwfslayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1192 1489 80.1 %
Date: 2024-05-04 12:52:34 Functions: 38 41 92.7 %

          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             :                "&lt;GmlObjectId&gt; 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 : }

Generated by: LCOV version 1.14