LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - ogrgmllayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 539 598 90.1 %
Date: 2025-10-22 13:51:22 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OGR
       4             :  * Purpose:  Implements OGRGMLLayer class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogr_gml.h"
      15             : #include "gmlutils.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_port.h"
      18             : #include "cpl_string.h"
      19             : #include "ogr_p.h"
      20             : #include "ogr_api.h"
      21             : 
      22             : #include <limits>
      23             : 
      24             : /************************************************************************/
      25             : /*                           OGRGMLLayer()                              */
      26             : /************************************************************************/
      27             : 
      28         713 : OGRGMLLayer::OGRGMLLayer(const char *pszName, bool bWriterIn,
      29         713 :                          OGRGMLDataSource *poDSIn)
      30             :     : poFeatureDefn(new OGRFeatureDefn(
      31         713 :           pszName + (STARTS_WITH_CI(pszName, "ogr:") ? 4 : 0))),
      32             :       bWriter(bWriterIn), poDS(poDSIn),
      33         713 :       poFClass(!bWriter ? poDS->GetReader()->GetClass(pszName) : nullptr),
      34             :       // Reader's should get the corresponding GMLFeatureClass and cache it.
      35        1426 :       hCacheSRS(GML_BuildOGRGeometryFromList_CreateCache()),
      36             :       // Compatibility option. Not advertized, because hopefully won't be
      37             :       // needed. Just put here in case.
      38             :       bUseOldFIDFormat(
      39         713 :           CPLTestBool(CPLGetConfigOption("GML_USE_OLD_FID_FORMAT", "FALSE"))),
      40             :       // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer()
      41             :       // and GMLReader::GMLReader().
      42             :       bFaceHoleNegative(
      43        2852 :           CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
      44             : {
      45         713 :     SetDescription(poFeatureDefn->GetName());
      46         713 :     poFeatureDefn->Reference();
      47         713 :     poFeatureDefn->SetGeomType(wkbNone);
      48         713 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                           ~OGRGMLLayer()                           */
      52             : /************************************************************************/
      53             : 
      54        1426 : OGRGMLLayer::~OGRGMLLayer()
      55             : 
      56             : {
      57         713 :     CPLFree(m_pszFIDPrefix);
      58             : 
      59         713 :     if (poFeatureDefn)
      60         713 :         poFeatureDefn->Release();
      61             : 
      62         713 :     GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
      63        1426 : }
      64             : 
      65             : /************************************************************************/
      66             : /*                            ResetReading()                            */
      67             : /************************************************************************/
      68             : 
      69         786 : void OGRGMLLayer::ResetReading()
      70             : 
      71             : {
      72         786 :     if (bWriter)
      73          16 :         return;
      74             : 
      75        1540 :     if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
      76         770 :         poDS->GetReadMode() == SEQUENTIAL_LAYERS)
      77             :     {
      78             :         // Does the last stored feature belong to our layer ? If so, no
      79             :         // need to reset the reader.
      80         100 :         if (m_iNextGMLId == 0 && poDS->PeekStoredGMLFeature() != nullptr &&
      81           0 :             poDS->PeekStoredGMLFeature()->GetClass() == poFClass)
      82           0 :             return;
      83             : 
      84         100 :         delete poDS->PeekStoredGMLFeature();
      85         100 :         poDS->SetStoredGMLFeature(nullptr);
      86             :     }
      87             : 
      88         770 :     m_iNextGMLId = 0;
      89         770 :     m_oSetFIDs.clear();
      90         770 :     poDS->GetReader()->ResetReading();
      91         770 :     CPLDebug("GML", "ResetReading()");
      92         770 :     if (poDS->GetLayerCount() > 1 && poDS->GetReadMode() == STANDARD)
      93             :     {
      94          78 :         const char *pszElementName = poFClass->GetElementName();
      95          78 :         const char *pszLastPipe = strrchr(pszElementName, '|');
      96          78 :         if (pszLastPipe != nullptr)
      97          33 :             pszElementName = pszLastPipe + 1;
      98          78 :         poDS->GetReader()->SetFilteredClassName(pszElementName);
      99             :     }
     100             : }
     101             : 
     102             : /************************************************************************/
     103             : /*                              Increment()                             */
     104             : /************************************************************************/
     105             : 
     106         755 : static GIntBig Increment(GIntBig nVal)
     107             : {
     108         755 :     if (nVal <= GINTBIG_MAX - 1)
     109         755 :         return nVal + 1;
     110           0 :     return nVal;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                           GetNextFeature()                           */
     115             : /************************************************************************/
     116             : 
     117         875 : OGRFeature *OGRGMLLayer::GetNextFeature()
     118             : 
     119             : {
     120         875 :     if (bWriter)
     121             :     {
     122          16 :         CPLError(CE_Failure, CPLE_NotSupported,
     123             :                  "Cannot read features when writing a GML file");
     124          16 :         return nullptr;
     125             :     }
     126             : 
     127         859 :     if (poDS->GetLastReadLayer() != this)
     128             :     {
     129         403 :         if (poDS->GetReadMode() != INTERLEAVED_LAYERS)
     130         394 :             ResetReading();
     131         403 :         poDS->SetLastReadLayer(this);
     132             :     }
     133             : 
     134         859 :     const bool bSkipCorruptedFeatures = CPLFetchBool(
     135         859 :         poDS->GetOpenOptions(), "SKIP_CORRUPTED_FEATURES",
     136         859 :         CPLTestBool(CPLGetConfigOption("GML_SKIP_CORRUPTED_FEATURES", "NO")));
     137             : 
     138             :     /* ==================================================================== */
     139             :     /*      Loop till we find and translate a feature meeting all our       */
     140             :     /*      requirements.                                                   */
     141             :     /* ==================================================================== */
     142             :     while (true)
     143             :     {
     144        1733 :         GMLFeature *poGMLFeature = poDS->PeekStoredGMLFeature();
     145        1733 :         if (poGMLFeature != nullptr)
     146             :         {
     147           3 :             poDS->SetStoredGMLFeature(nullptr);
     148             :         }
     149             :         else
     150             :         {
     151        1730 :             poGMLFeature = poDS->GetReader()->NextFeature();
     152        1730 :             if (poGMLFeature == nullptr)
     153         859 :                 return nullptr;
     154             : 
     155             :             // We count reading low level GML features as a feature read for
     156             :             // work checking purposes, though at least we didn't necessary
     157             :             // have to turn it into an OGRFeature.
     158        1590 :             m_nFeaturesRead++;
     159             :         }
     160             : 
     161             :         /* --------------------------------------------------------------------
     162             :          */
     163             :         /*      Is it of the proper feature class? */
     164             :         /* --------------------------------------------------------------------
     165             :          */
     166             : 
     167        1593 :         if (poGMLFeature->GetClass() != poFClass)
     168             :         {
     169        2478 :             if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
     170        1650 :                 (poDS->GetReadMode() == SEQUENTIAL_LAYERS && m_iNextGMLId != 0))
     171             :             {
     172           4 :                 CPLAssert(poDS->PeekStoredGMLFeature() == nullptr);
     173           4 :                 poDS->SetStoredGMLFeature(poGMLFeature);
     174           4 :                 return nullptr;
     175             :             }
     176             :             else
     177             :             {
     178         824 :                 delete poGMLFeature;
     179         824 :                 continue;
     180             :             }
     181             :         }
     182             : 
     183             :         /* --------------------------------------------------------------------
     184             :          */
     185             :         /*      Extract the fid: */
     186             :         /*      -Assumes the fids are non-negative integers with an optional */
     187             :         /*       prefix */
     188             :         /*      -If a prefix differs from the prefix of the first feature from
     189             :          */
     190             :         /*       the poDS then the fids from the poDS are ignored and are */
     191             :         /*       assigned serially thereafter */
     192             :         /* --------------------------------------------------------------------
     193             :          */
     194         765 :         GIntBig nFID = -1;
     195         765 :         constexpr size_t MAX_FID_DIGIT_COUNT = 20;
     196         765 :         const char *pszGML_FID = poGMLFeature->GetFID();
     197         765 :         if (m_bInvalidFIDFound || pszGML_FID == nullptr || pszGML_FID[0] == 0)
     198             :         {
     199             :             // do nothing
     200             :         }
     201         607 :         else if (m_iNextGMLId == 0)
     202             :         {
     203         315 :             size_t j = 0;
     204         315 :             size_t i = strlen(pszGML_FID);
     205         753 :             while (i > 0 && j < MAX_FID_DIGIT_COUNT)
     206             :             {
     207         753 :                 --i;
     208         753 :                 if (!(pszGML_FID[i] >= '0' && pszGML_FID[i] <= '9'))
     209             :                     break;
     210         443 :                 j++;
     211         443 :                 if (i == 0)
     212             :                 {
     213           5 :                     i = std::numeric_limits<size_t>::max();
     214           5 :                     break;
     215             :                 }
     216             :             }
     217             :             // i points the last character of the fid prefix.
     218         625 :             if (i != std::numeric_limits<size_t>::max() &&
     219         625 :                 j < MAX_FID_DIGIT_COUNT && m_pszFIDPrefix == nullptr)
     220             :             {
     221         246 :                 m_pszFIDPrefix = static_cast<char *>(CPLMalloc(i + 2));
     222         246 :                 memcpy(m_pszFIDPrefix, pszGML_FID, i + 1);
     223         246 :                 m_pszFIDPrefix[i + 1] = '\0';
     224             :             }
     225             :             // m_pszFIDPrefix now contains the prefix or NULL if no prefix is
     226             :             // found.
     227         315 :             if (j < MAX_FID_DIGIT_COUNT)
     228             :             {
     229         315 :                 char *endptr = nullptr;
     230         315 :                 nFID = std::strtoll(
     231         315 :                     pszGML_FID +
     232         315 :                         (i != std::numeric_limits<size_t>::max() ? i + 1 : 0),
     233             :                     &endptr, 10);
     234         315 :                 if (endptr == pszGML_FID + strlen(pszGML_FID))
     235             :                 {
     236         315 :                     if (m_iNextGMLId <= nFID)
     237         315 :                         m_iNextGMLId = Increment(nFID);
     238             :                 }
     239             :                 else
     240             :                 {
     241           0 :                     nFID = -1;
     242             :                 }
     243             :             }
     244             :         }
     245             :         else  // if( iNextGMLId != 0 ).
     246             :         {
     247         292 :             const char *pszFIDPrefix_notnull = m_pszFIDPrefix;
     248         292 :             if (pszFIDPrefix_notnull == nullptr)
     249           9 :                 pszFIDPrefix_notnull = "";
     250         292 :             const size_t nLenPrefix = strlen(pszFIDPrefix_notnull);
     251             : 
     252         292 :             if (strncmp(pszGML_FID, pszFIDPrefix_notnull, nLenPrefix) == 0 &&
     253         287 :                 strlen(pszGML_FID + nLenPrefix) < MAX_FID_DIGIT_COUNT)
     254             :             {
     255         287 :                 char *endptr = nullptr;
     256         287 :                 nFID = std::strtoll(pszGML_FID + nLenPrefix, &endptr, 10);
     257         287 :                 if (endptr == pszGML_FID + strlen(pszGML_FID))
     258             :                 {
     259             :                     // fid with the prefix. Using its numerical part.
     260         285 :                     if (m_iNextGMLId <= nFID)
     261         274 :                         m_iNextGMLId = Increment(nFID);
     262             :                 }
     263             :                 else
     264             :                 {
     265           2 :                     nFID = -1;
     266             :                 }
     267             :             }
     268             :         }
     269             : 
     270         765 :         constexpr size_t MAX_FID_SET_SIZE = 10 * 1000 * 1000;
     271         765 :         if (nFID >= 0 && m_oSetFIDs.size() < MAX_FID_SET_SIZE)
     272             :         {
     273             :             // Make sure FIDs are unique
     274         600 :             if (!cpl::contains(m_oSetFIDs, nFID))
     275         599 :                 m_oSetFIDs.insert(nFID);
     276             :             else
     277             :             {
     278           1 :                 m_oSetFIDs.clear();
     279           1 :                 nFID = -1;
     280             :             }
     281             :         }
     282             : 
     283         765 :         if (nFID < 0)
     284             :         {
     285             :             // fid without the aforementioned prefix or a valid numerical
     286             :             // part.
     287         166 :             m_bInvalidFIDFound = true;
     288         166 :             nFID = m_iNextGMLId;
     289         166 :             m_iNextGMLId = Increment(m_iNextGMLId);
     290             :         }
     291             : 
     292             :         /* --------------------------------------------------------------------
     293             :          */
     294             :         /*      Does it satisfy the spatial query, if there is one? */
     295             :         /* --------------------------------------------------------------------
     296             :          */
     297             : 
     298         765 :         OGRGeometry **papoGeometries = nullptr;
     299         765 :         const CPLXMLNode *const *papsGeometry = poGMLFeature->GetGeometryList();
     300             : 
     301         765 :         const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
     302             :         const CPLXMLNode *psBoundedByGeometry =
     303         765 :             poGMLFeature->GetBoundedByGeometry();
     304         765 :         if (psBoundedByGeometry && !(papsGeometry && papsGeometry[0]))
     305             :         {
     306           6 :             apsGeometries[0] = psBoundedByGeometry;
     307           6 :             papsGeometry = apsGeometries;
     308             :         }
     309             : 
     310         765 :         OGRGeometry *poGeom = nullptr;
     311             : 
     312         765 :         if (poFeatureDefn->GetGeomFieldCount() > 1)
     313             :         {
     314         188 :             papoGeometries = static_cast<OGRGeometry **>(CPLCalloc(
     315          94 :                 poFeatureDefn->GetGeomFieldCount(), sizeof(OGRGeometry *)));
     316          94 :             const char *pszSRSName = poDS->GetGlobalSRSName();
     317         301 :             for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     318             :             {
     319         207 :                 const CPLXMLNode *psGeom = poGMLFeature->GetGeometryRef(i);
     320         207 :                 if (psGeom != nullptr)
     321             :                 {
     322         163 :                     const CPLXMLNode *myGeometryList[2] = {psGeom, nullptr};
     323         163 :                     poGeom = GML_BuildOGRGeometryFromList(
     324             :                         myGeometryList, true,
     325         163 :                         poDS->GetInvertAxisOrderIfLatLong(), pszSRSName,
     326         163 :                         poDS->GetConsiderEPSGAsURN(),
     327         163 :                         poDS->GetSwapCoordinates(),
     328         163 :                         poDS->GetSecondaryGeometryOption(), hCacheSRS,
     329         163 :                         bFaceHoleNegative);
     330             : 
     331             :                     // Do geometry type changes if needed to match layer
     332             :                     // geometry type.
     333         163 :                     if (poGeom != nullptr)
     334             :                     {
     335         163 :                         papoGeometries[i] = OGRGeometryFactory::forceTo(
     336             :                             poGeom,
     337         163 :                             poFeatureDefn->GetGeomFieldDefn(i)->GetType());
     338         163 :                         poGeom = nullptr;
     339             :                     }
     340             :                     else
     341             :                     {
     342             :                         // We assume the createFromGML() function would have
     343             :                         // already reported the error.
     344           0 :                         for (int j = 0; j < poFeatureDefn->GetGeomFieldCount();
     345             :                              j++)
     346             :                         {
     347           0 :                             delete papoGeometries[j];
     348             :                         }
     349           0 :                         CPLFree(papoGeometries);
     350           0 :                         delete poGMLFeature;
     351           0 :                         return nullptr;
     352             :                     }
     353             :                 }
     354             :             }
     355             : 
     356           0 :             if (m_poFilterGeom != nullptr && m_iGeomFieldFilter >= 0 &&
     357           0 :                 m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount() &&
     358          94 :                 papoGeometries[m_iGeomFieldFilter] &&
     359           0 :                 !FilterGeometry(papoGeometries[m_iGeomFieldFilter]))
     360             :             {
     361           0 :                 for (int j = 0; j < poFeatureDefn->GetGeomFieldCount(); j++)
     362             :                 {
     363           0 :                     delete papoGeometries[j];
     364             :                 }
     365           0 :                 CPLFree(papoGeometries);
     366           0 :                 delete poGMLFeature;
     367           0 :                 continue;
     368             :             }
     369             :         }
     370         671 :         else if (papsGeometry[0] &&
     371         555 :                  strcmp(papsGeometry[0]->pszValue, "null") == 0)
     372             :         {
     373             :             // do nothing
     374             :         }
     375         667 :         else if (papsGeometry[0] != nullptr)
     376             :         {
     377         551 :             const char *pszSRSName = poDS->GetGlobalSRSName();
     378         551 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     379         551 :             poGeom = GML_BuildOGRGeometryFromList(
     380         551 :                 papsGeometry, true, poDS->GetInvertAxisOrderIfLatLong(),
     381         551 :                 pszSRSName, poDS->GetConsiderEPSGAsURN(),
     382         551 :                 poDS->GetSwapCoordinates(), poDS->GetSecondaryGeometryOption(),
     383         551 :                 hCacheSRS, bFaceHoleNegative);
     384         551 :             CPLPopErrorHandler();
     385             : 
     386             :             // Do geometry type changes if needed to match layer geometry type.
     387         551 :             if (poGeom != nullptr)
     388             :             {
     389         547 :                 poGeom = OGRGeometryFactory::forceTo(poGeom, GetGeomType());
     390             :             }
     391             :             else
     392             :             {
     393           4 :                 const CPLString osLastErrorMsg(CPLGetLastErrorMsg());
     394             : 
     395           8 :                 CPLError(
     396             :                     bSkipCorruptedFeatures ? CE_Warning : CE_Failure,
     397             :                     CPLE_AppDefined,
     398             :                     "Geometry of feature " CPL_FRMT_GIB
     399             :                     " %scannot be parsed: %s%s",
     400           4 :                     nFID, pszGML_FID ? CPLSPrintf("%s ", pszGML_FID) : "",
     401             :                     osLastErrorMsg.c_str(),
     402             :                     bSkipCorruptedFeatures
     403             :                         ? ". Skipping to next feature."
     404             :                         : ". You may set the GML_SKIP_CORRUPTED_FEATURES "
     405             :                           "configuration option to YES to skip to the next "
     406             :                           "feature");
     407           4 :                 delete poGMLFeature;
     408           4 :                 if (bSkipCorruptedFeatures)
     409           2 :                     continue;
     410           2 :                 return nullptr;
     411             :             }
     412             : 
     413         547 :             if (m_poFilterGeom != nullptr && !FilterGeometry(poGeom))
     414             :             {
     415          20 :                 delete poGMLFeature;
     416          20 :                 delete poGeom;
     417          20 :                 continue;
     418             :             }
     419             :         }
     420             : 
     421             :         /* --------------------------------------------------------------------
     422             :          */
     423             :         /*      Convert the whole feature into an OGRFeature. */
     424             :         /* --------------------------------------------------------------------
     425             :          */
     426         741 :         int iDstField = 0;
     427         741 :         OGRFeature *poOGRFeature = new OGRFeature(poFeatureDefn);
     428             : 
     429         741 :         poOGRFeature->SetFID(nFID);
     430         741 :         if (poDS->ExposeId())
     431             :         {
     432         633 :             if (pszGML_FID)
     433         590 :                 poOGRFeature->SetField(iDstField, pszGML_FID);
     434         633 :             iDstField++;
     435             :         }
     436             : 
     437         741 :         const int nPropertyCount = poFClass->GetPropertyCount();
     438        3524 :         for (int iField = 0; iField < nPropertyCount; iField++, iDstField++)
     439             :         {
     440             :             const GMLProperty *psGMLProperty =
     441        2783 :                 poGMLFeature->GetProperty(iField);
     442        2783 :             if (psGMLProperty == nullptr || psGMLProperty->nSubProperties == 0)
     443         616 :                 continue;
     444             : 
     445        2167 :             if (EQUAL(psGMLProperty->papszSubProperties[0], OGR_GML_NULL))
     446             :             {
     447           8 :                 poOGRFeature->SetFieldNull(iDstField);
     448           8 :                 continue;
     449             :             }
     450             : 
     451        2159 :             switch (poFClass->GetProperty(iField)->GetType())
     452             :             {
     453         361 :                 case GMLPT_Real:
     454             :                 {
     455         361 :                     poOGRFeature->SetField(
     456             :                         iDstField,
     457         361 :                         CPLAtof(psGMLProperty->papszSubProperties[0]));
     458             :                 }
     459         361 :                 break;
     460             : 
     461          13 :                 case GMLPT_IntegerList:
     462             :                 {
     463          13 :                     const int nCount = psGMLProperty->nSubProperties;
     464             :                     int *panIntList =
     465          13 :                         static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
     466             : 
     467          39 :                     for (int i = 0; i < nCount; i++)
     468          26 :                         panIntList[i] =
     469          26 :                             atoi(psGMLProperty->papszSubProperties[i]);
     470             : 
     471          13 :                     poOGRFeature->SetField(iDstField, nCount, panIntList);
     472          13 :                     CPLFree(panIntList);
     473             :                 }
     474          13 :                 break;
     475             : 
     476           6 :                 case GMLPT_Integer64List:
     477             :                 {
     478           6 :                     const int nCount = psGMLProperty->nSubProperties;
     479             :                     GIntBig *panIntList = static_cast<GIntBig *>(
     480           6 :                         CPLMalloc(sizeof(GIntBig) * nCount));
     481             : 
     482          15 :                     for (int i = 0; i < nCount; i++)
     483          18 :                         panIntList[i] =
     484           9 :                             CPLAtoGIntBig(psGMLProperty->papszSubProperties[i]);
     485             : 
     486           6 :                     poOGRFeature->SetField(iDstField, nCount, panIntList);
     487           6 :                     CPLFree(panIntList);
     488             :                 }
     489           6 :                 break;
     490             : 
     491          25 :                 case GMLPT_RealList:
     492             :                 {
     493          25 :                     const int nCount = psGMLProperty->nSubProperties;
     494             :                     double *padfList = static_cast<double *>(
     495          25 :                         CPLMalloc(sizeof(double) * nCount));
     496             : 
     497          59 :                     for (int i = 0; i < nCount; i++)
     498          68 :                         padfList[i] =
     499          34 :                             CPLAtof(psGMLProperty->papszSubProperties[i]);
     500             : 
     501          25 :                     poOGRFeature->SetField(iDstField, nCount, padfList);
     502          25 :                     CPLFree(padfList);
     503             :                 }
     504          25 :                 break;
     505             : 
     506         106 :                 case GMLPT_StringList:
     507             :                 case GMLPT_FeaturePropertyList:
     508             :                 {
     509         106 :                     poOGRFeature->SetField(iDstField,
     510         106 :                                            psGMLProperty->papszSubProperties);
     511             :                 }
     512         106 :                 break;
     513             : 
     514          46 :                 case GMLPT_Boolean:
     515             :                 {
     516          46 :                     if (strcmp(psGMLProperty->papszSubProperties[0], "true") ==
     517           7 :                             0 ||
     518           7 :                         strcmp(psGMLProperty->papszSubProperties[0], "1") == 0)
     519             :                     {
     520          39 :                         poOGRFeature->SetField(iDstField, 1);
     521             :                     }
     522           7 :                     else if (strcmp(psGMLProperty->papszSubProperties[0],
     523           0 :                                     "false") == 0 ||
     524           0 :                              strcmp(psGMLProperty->papszSubProperties[0],
     525             :                                     "0") == 0)
     526             :                     {
     527           7 :                         poOGRFeature->SetField(iDstField, 0);
     528             :                     }
     529             :                     else
     530             :                     {
     531           0 :                         poOGRFeature->SetField(
     532           0 :                             iDstField, psGMLProperty->papszSubProperties[0]);
     533             :                     }
     534          46 :                     break;
     535             :                 }
     536             : 
     537           3 :                 case GMLPT_BooleanList:
     538             :                 {
     539           3 :                     const int nCount = psGMLProperty->nSubProperties;
     540             :                     int *panIntList =
     541           3 :                         static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
     542             : 
     543           9 :                     for (int i = 0; i < nCount; i++)
     544             :                     {
     545           6 :                         panIntList[i] =
     546           6 :                             (strcmp(psGMLProperty->papszSubProperties[i],
     547           9 :                                     "true") == 0 ||
     548           3 :                              strcmp(psGMLProperty->papszSubProperties[i],
     549             :                                     "1") == 0);
     550             :                     }
     551             : 
     552           3 :                     poOGRFeature->SetField(iDstField, nCount, panIntList);
     553           3 :                     CPLFree(panIntList);
     554           3 :                     break;
     555             :                 }
     556             : 
     557        1599 :                 default:
     558        1599 :                     poOGRFeature->SetField(
     559        1599 :                         iDstField, psGMLProperty->papszSubProperties[0]);
     560        1599 :                     break;
     561             :             }
     562             :         }
     563             : 
     564         741 :         delete poGMLFeature;
     565         741 :         poGMLFeature = nullptr;
     566             : 
     567             :         // Assign the geometry before the attribute filter because
     568             :         // the attribute filter may use a special field like OGR_GEOMETRY.
     569         741 :         if (papoGeometries != nullptr)
     570             :         {
     571         301 :             for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     572             :             {
     573         207 :                 poOGRFeature->SetGeomFieldDirectly(i, papoGeometries[i]);
     574             :             }
     575          94 :             CPLFree(papoGeometries);
     576          94 :             papoGeometries = nullptr;
     577             :         }
     578             :         else
     579             :         {
     580         647 :             poOGRFeature->SetGeometryDirectly(poGeom);
     581             :         }
     582             : 
     583             :         // Assign SRS.
     584        1518 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     585             :         {
     586         777 :             poGeom = poOGRFeature->GetGeomFieldRef(i);
     587         777 :             if (poGeom != nullptr)
     588             :             {
     589             :                 const OGRSpatialReference *poSRS =
     590         690 :                     poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef();
     591         690 :                 if (poSRS != nullptr)
     592         147 :                     poGeom->assignSpatialReference(poSRS);
     593             :             }
     594             :         }
     595             : 
     596             :         /* --------------------------------------------------------------------
     597             :          */
     598             :         /*      Test against the attribute query. */
     599             :         /* --------------------------------------------------------------------
     600             :          */
     601         741 :         if (m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poOGRFeature))
     602             :         {
     603          28 :             delete poOGRFeature;
     604          28 :             continue;
     605             :         }
     606             : 
     607             :         // Got the desired feature.
     608         713 :         return poOGRFeature;
     609         874 :     }
     610             : }
     611             : 
     612             : /************************************************************************/
     613             : /*                          GetFeatureCount()                           */
     614             : /************************************************************************/
     615             : 
     616          62 : GIntBig OGRGMLLayer::GetFeatureCount(int bForce)
     617             : 
     618             : {
     619          62 :     if (poFClass == nullptr)
     620           0 :         return 0;
     621             : 
     622          62 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     623           6 :         return OGRLayer::GetFeatureCount(bForce);
     624             : 
     625             :     // If the schema is read from a .xsd file, we haven't read
     626             :     // the feature count, so compute it now.
     627          56 :     GIntBig nFeatureCount = poFClass->GetFeatureCount();
     628          56 :     if (nFeatureCount < 0)
     629             :     {
     630          26 :         nFeatureCount = OGRLayer::GetFeatureCount(bForce);
     631          26 :         poFClass->SetFeatureCount(nFeatureCount);
     632             :     }
     633             : 
     634          56 :     return nFeatureCount;
     635             : }
     636             : 
     637             : /************************************************************************/
     638             : /*                            IGetExtent()                              */
     639             : /************************************************************************/
     640             : 
     641          10 : OGRErr OGRGMLLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
     642             :                                bool bForce)
     643             : 
     644             : {
     645          10 :     if (GetGeomType() == wkbNone)
     646           0 :         return OGRERR_FAILURE;
     647             : 
     648          10 :     double dfXMin = 0.0;
     649          10 :     double dfXMax = 0.0;
     650          10 :     double dfYMin = 0.0;
     651          10 :     double dfYMax = 0.0;
     652          20 :     if (poFClass != nullptr &&
     653          10 :         poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
     654             :     {
     655           8 :         psExtent->MinX = dfXMin;
     656           8 :         psExtent->MaxX = dfXMax;
     657           8 :         psExtent->MinY = dfYMin;
     658           8 :         psExtent->MaxY = dfYMax;
     659             : 
     660           8 :         return OGRERR_NONE;
     661             :     }
     662             : 
     663           2 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     664             : }
     665             : 
     666             : /************************************************************************/
     667             : /*                             GetExtent()                              */
     668             : /************************************************************************/
     669             : 
     670         505 : static void GMLWriteField(OGRGMLDataSource *poDS, VSILFILE *fp,
     671             :                           bool bWriteSpaceIndentation, const char *pszPrefix,
     672             :                           bool bRemoveAppPrefix, OGRFieldDefn *poFieldDefn,
     673             :                           const char *pszVal)
     674             : 
     675             : {
     676         505 :     const char *pszFieldName = poFieldDefn->GetNameRef();
     677             : 
     678         505 :     while (*pszVal == ' ')
     679           0 :         pszVal++;
     680             : 
     681         505 :     if (bWriteSpaceIndentation)
     682         505 :         VSIFPrintfL(fp, "      ");
     683             : 
     684         505 :     if (bRemoveAppPrefix)
     685          60 :         poDS->PrintLine(fp, "<%s>%s</%s>", pszFieldName, pszVal, pszFieldName);
     686             :     else
     687         445 :         poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix, pszFieldName,
     688             :                         pszVal, pszPrefix, pszFieldName);
     689         505 : }
     690             : 
     691             : /************************************************************************/
     692             : /*                           ICreateFeature()                            */
     693             : /************************************************************************/
     694             : 
     695         266 : OGRErr OGRGMLLayer::ICreateFeature(OGRFeature *poFeature)
     696             : 
     697             : {
     698         266 :     const bool bIsGML3Output = poDS->IsGML3Output();
     699         266 :     VSILFILE *fp = poDS->GetOutputFP();
     700         266 :     const bool bWriteSpaceIndentation = poDS->WriteSpaceIndentation();
     701         266 :     const char *pszPrefix = poDS->GetAppPrefix();
     702         266 :     const bool bRemoveAppPrefix = poDS->RemoveAppPrefix();
     703         266 :     const bool bGMLFeatureCollection = poDS->GMLFeatureCollection();
     704             : 
     705         266 :     if (!bWriter || poDS->HasWriteError())
     706           0 :         return OGRERR_FAILURE;
     707             : 
     708         266 :     poFeature->FillUnsetWithDefault(TRUE, nullptr);
     709         266 :     if (!poFeature->Validate(OGR_F_VAL_ALL & ~OGR_F_VAL_GEOM_TYPE &
     710             :                                  ~OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT,
     711             :                              TRUE))
     712           2 :         return OGRERR_FAILURE;
     713             : 
     714         264 :     if (bWriteSpaceIndentation)
     715         264 :         VSIFPrintfL(fp, "  ");
     716         264 :     if (bIsGML3Output && !bGMLFeatureCollection)
     717             :     {
     718         220 :         if (bRemoveAppPrefix)
     719          10 :             poDS->PrintLine(fp, "<featureMember>");
     720             :         else
     721         210 :             poDS->PrintLine(fp, "<%s:featureMember>", pszPrefix);
     722             :     }
     723             :     else
     724             :     {
     725          44 :         poDS->PrintLine(fp, "<gml:featureMember>");
     726             :     }
     727             : 
     728         264 :     if (poFeature->GetFID() == OGRNullFID)
     729         251 :         poFeature->SetFID(m_iNextGMLId++);
     730             : 
     731         264 :     if (bWriteSpaceIndentation)
     732         264 :         VSIFPrintfL(fp, "    ");
     733         264 :     VSIFPrintfL(fp, "<");
     734         264 :     if (!bRemoveAppPrefix)
     735         244 :         VSIFPrintfL(fp, "%s:", pszPrefix);
     736             : 
     737         264 :     int nGMLIdIndex = -1;
     738         264 :     if (bIsGML3Output)
     739             :     {
     740         230 :         nGMLIdIndex = poFeatureDefn->GetFieldIndex("gml_id");
     741         230 :         if (nGMLIdIndex >= 0 && poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
     742           3 :             poDS->PrintLine(fp, "%s gml:id=\"%s\">", poFeatureDefn->GetName(),
     743             :                             poFeature->GetFieldAsString(nGMLIdIndex));
     744             :         else
     745         454 :             poDS->PrintLine(fp, "%s gml:id=\"%s." CPL_FRMT_GIB "\">",
     746         227 :                             poFeatureDefn->GetName(), poFeatureDefn->GetName(),
     747             :                             poFeature->GetFID());
     748             :     }
     749             :     else
     750             :     {
     751          34 :         nGMLIdIndex = poFeatureDefn->GetFieldIndex("fid");
     752          34 :         if (bUseOldFIDFormat)
     753             :         {
     754           0 :             poDS->PrintLine(fp, "%s fid=\"F" CPL_FRMT_GIB "\">",
     755           0 :                             poFeatureDefn->GetName(), poFeature->GetFID());
     756             :         }
     757          44 :         else if (nGMLIdIndex >= 0 &&
     758          10 :                  poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
     759             :         {
     760          10 :             poDS->PrintLine(fp, "%s fid=\"%s\">", poFeatureDefn->GetName(),
     761             :                             poFeature->GetFieldAsString(nGMLIdIndex));
     762             :         }
     763             :         else
     764             :         {
     765          48 :             poDS->PrintLine(fp, "%s fid=\"%s." CPL_FRMT_GIB "\">",
     766          24 :                             poFeatureDefn->GetName(), poFeatureDefn->GetName(),
     767             :                             poFeature->GetFID());
     768             :         }
     769             :     }
     770             : 
     771         517 :     for (int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount();
     772             :          iGeomField++)
     773             :     {
     774             :         const OGRGeomFieldDefn *poFieldDefn =
     775         253 :             poFeatureDefn->GetGeomFieldDefn(iGeomField);
     776             : 
     777             :         // Write out Geometry - for now it isn't indented properly.
     778             :         // GML geometries don't like very much the concept of empty geometry.
     779         253 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField);
     780         253 :         if (poGeom != nullptr && !poGeom->IsEmpty())
     781             :         {
     782         215 :             OGREnvelope3D sGeomBounds;
     783             : 
     784         215 :             const int nCoordDimension = poGeom->getCoordinateDimension();
     785             : 
     786         215 :             poGeom->getEnvelope(&sGeomBounds);
     787         215 :             if (poDS->HasWriteGlobalSRS())
     788         211 :                 poDS->GrowExtents(&sGeomBounds, nCoordDimension);
     789             : 
     790         356 :             if (poGeom->getSpatialReference() == nullptr &&
     791         141 :                 poFieldDefn->GetSpatialRef() != nullptr)
     792          18 :                 poGeom->assignSpatialReference(poFieldDefn->GetSpatialRef());
     793             : 
     794         215 :             const auto &oCoordPrec = poFieldDefn->GetCoordinatePrecision();
     795             : 
     796         215 :             if (bIsGML3Output && poDS->WriteFeatureBoundedBy())
     797             :             {
     798         171 :                 bool bCoordSwap = false;
     799             : 
     800             :                 char *pszSRSName =
     801         171 :                     GML_GetSRSName(poGeom->getSpatialReference(),
     802         171 :                                    poDS->GetSRSNameFormat(), &bCoordSwap);
     803         171 :                 char szLowerCorner[75] = {};
     804         171 :                 char szUpperCorner[75] = {};
     805             : 
     806         171 :                 OGRWktOptions coordOpts;
     807             : 
     808         171 :                 if (oCoordPrec.dfXYResolution !=
     809             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     810             :                 {
     811           3 :                     coordOpts.format = OGRWktFormat::F;
     812           3 :                     coordOpts.xyPrecision =
     813           3 :                         OGRGeomCoordinatePrecision::ResolutionToPrecision(
     814           3 :                             oCoordPrec.dfXYResolution);
     815             :                 }
     816         171 :                 if (oCoordPrec.dfZResolution !=
     817             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     818             :                 {
     819           3 :                     coordOpts.format = OGRWktFormat::F;
     820           3 :                     coordOpts.zPrecision =
     821           3 :                         OGRGeomCoordinatePrecision::ResolutionToPrecision(
     822           3 :                             oCoordPrec.dfZResolution);
     823             :                 }
     824             : 
     825         342 :                 std::string wkt;
     826         171 :                 if (bCoordSwap)
     827             :                 {
     828          36 :                     wkt = OGRMakeWktCoordinate(
     829             :                         sGeomBounds.MinY, sGeomBounds.MinX, sGeomBounds.MinZ,
     830          18 :                         nCoordDimension, coordOpts);
     831          18 :                     memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
     832             : 
     833          36 :                     wkt = OGRMakeWktCoordinate(
     834             :                         sGeomBounds.MaxY, sGeomBounds.MaxX, sGeomBounds.MaxZ,
     835          18 :                         nCoordDimension, coordOpts);
     836          18 :                     memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
     837             :                 }
     838             :                 else
     839             :                 {
     840         306 :                     wkt = OGRMakeWktCoordinate(
     841             :                         sGeomBounds.MinX, sGeomBounds.MinY, sGeomBounds.MinZ,
     842         153 :                         nCoordDimension, coordOpts);
     843         153 :                     memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
     844             : 
     845         306 :                     wkt = OGRMakeWktCoordinate(
     846             :                         sGeomBounds.MaxX, sGeomBounds.MaxY, sGeomBounds.MaxZ,
     847         153 :                         nCoordDimension, coordOpts);
     848         153 :                     memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
     849             :                 }
     850         171 :                 if (bWriteSpaceIndentation)
     851         171 :                     VSIFPrintfL(fp, "      ");
     852         171 :                 poDS->PrintLine(
     853             :                     fp,
     854             :                     "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s"
     855             :                     "</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner>"
     856             :                     "</gml:Envelope></gml:boundedBy>",
     857             :                     (nCoordDimension == 3) ? " srsDimension=\"3\"" : "",
     858             :                     pszSRSName, szLowerCorner, szUpperCorner);
     859         171 :                 CPLFree(pszSRSName);
     860             :             }
     861             : 
     862         215 :             char **papszOptions = nullptr;
     863         215 :             if (bIsGML3Output)
     864             :             {
     865         181 :                 papszOptions = CSLAddString(papszOptions, "FORMAT=GML3");
     866         181 :                 if (poDS->GetSRSNameFormat() == SRSNAME_SHORT)
     867             :                     papszOptions =
     868           1 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=SHORT");
     869         180 :                 else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URN)
     870             :                     papszOptions =
     871         167 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URN");
     872          13 :                 else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URL)
     873             :                     papszOptions =
     874          13 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URL");
     875             :             }
     876         215 :             const char *pszSRSDimensionLoc = poDS->GetSRSDimensionLoc();
     877         215 :             if (pszSRSDimensionLoc != nullptr)
     878           3 :                 papszOptions = CSLSetNameValue(papszOptions, "SRSDIMENSION_LOC",
     879             :                                                pszSRSDimensionLoc);
     880         215 :             if (poDS->IsGML32Output())
     881             :             {
     882         115 :                 if (poFeatureDefn->GetGeomFieldCount() > 1)
     883          18 :                     papszOptions = CSLAddString(
     884             :                         papszOptions, CPLSPrintf("GMLID=%s.%s." CPL_FRMT_GIB,
     885           9 :                                                  poFeatureDefn->GetName(),
     886             :                                                  poFieldDefn->GetNameRef(),
     887             :                                                  poFeature->GetFID()));
     888             :                 else
     889         212 :                     papszOptions = CSLAddString(
     890             :                         papszOptions, CPLSPrintf("GMLID=%s.geom." CPL_FRMT_GIB,
     891         106 :                                                  poFeatureDefn->GetName(),
     892             :                                                  poFeature->GetFID()));
     893             :             }
     894             : 
     895         215 :             if (oCoordPrec.dfXYResolution !=
     896             :                 OGRGeomCoordinatePrecision::UNKNOWN)
     897             :             {
     898           3 :                 papszOptions = CSLAddString(
     899             :                     papszOptions, CPLSPrintf("XY_COORD_RESOLUTION=%g",
     900           3 :                                              oCoordPrec.dfXYResolution));
     901             :             }
     902         215 :             if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     903             :             {
     904           3 :                 papszOptions = CSLAddString(
     905             :                     papszOptions, CPLSPrintf("Z_COORD_RESOLUTION=%g",
     906           3 :                                              oCoordPrec.dfZResolution));
     907             :             }
     908             : 
     909         215 :             char *pszGeometry = nullptr;
     910         215 :             if (!bIsGML3Output && OGR_GT_IsNonLinear(poGeom->getGeometryType()))
     911             :             {
     912           0 :                 OGRGeometry *poGeomTmp = OGRGeometryFactory::forceTo(
     913           0 :                     poGeom->clone(),
     914           0 :                     OGR_GT_GetLinear(poGeom->getGeometryType()));
     915           0 :                 pszGeometry = poGeomTmp->exportToGML(papszOptions);
     916           0 :                 delete poGeomTmp;
     917             :             }
     918             :             else
     919             :             {
     920         215 :                 if (wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
     921             :                 {
     922           0 :                     pszGeometry = poGeom->exportToGML(papszOptions);
     923             : 
     924             :                     const char *pszGMLID =
     925           0 :                         poDS->IsGML32Output()
     926           0 :                             ? CPLSPrintf(
     927             :                                   " gml:id=\"%s\"",
     928             :                                   CSLFetchNameValue(papszOptions, "GMLID"))
     929           0 :                             : "";
     930           0 :                     char *pszNewGeom = CPLStrdup(
     931             :                         CPLSPrintf("<gml:TriangulatedSurface%s><gml:patches>%s<"
     932             :                                    "/gml:patches></gml:TriangulatedSurface>",
     933             :                                    pszGMLID, pszGeometry));
     934           0 :                     CPLFree(pszGeometry);
     935           0 :                     pszGeometry = pszNewGeom;
     936             :                 }
     937             :                 else
     938             :                 {
     939         215 :                     pszGeometry = poGeom->exportToGML(papszOptions);
     940             :                 }
     941             :             }
     942         215 :             CSLDestroy(papszOptions);
     943         215 :             if (pszGeometry)
     944             :             {
     945         214 :                 if (bWriteSpaceIndentation)
     946         214 :                     VSIFPrintfL(fp, "      ");
     947         214 :                 if (bRemoveAppPrefix)
     948          20 :                     poDS->PrintLine(fp, "<%s>%s</%s>",
     949             :                                     poFieldDefn->GetNameRef(), pszGeometry,
     950             :                                     poFieldDefn->GetNameRef());
     951             :                 else
     952         194 :                     poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix,
     953             :                                     poFieldDefn->GetNameRef(), pszGeometry,
     954             :                                     pszPrefix, poFieldDefn->GetNameRef());
     955             :             }
     956             :             else
     957             :             {
     958           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     959             :                          "Export of geometry to GML failed");
     960             :             }
     961         215 :             CPLFree(pszGeometry);
     962             :         }
     963             :     }
     964             : 
     965             :     // Write all "set" fields.
     966         885 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     967             :     {
     968         621 :         if (iField == nGMLIdIndex)
     969          13 :             continue;
     970         608 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
     971             : 
     972         608 :         if (poFeature->IsFieldNull(iField))
     973             :         {
     974           1 :             const char *pszFieldName = poFieldDefn->GetNameRef();
     975             : 
     976           1 :             if (bWriteSpaceIndentation)
     977           1 :                 VSIFPrintfL(fp, "      ");
     978             : 
     979           1 :             if (bRemoveAppPrefix)
     980           0 :                 poDS->PrintLine(fp, "<%s xsi:nil=\"true\"/>", pszFieldName);
     981             :             else
     982           1 :                 poDS->PrintLine(fp, "<%s:%s xsi:nil=\"true\"/>", pszPrefix,
     983             :                                 pszFieldName);
     984             :         }
     985         607 :         else if (poFeature->IsFieldSet(iField))
     986             :         {
     987         500 :             OGRFieldType eType = poFieldDefn->GetType();
     988         500 :             if (eType == OFTStringList)
     989             :             {
     990           1 :                 char **papszIter = poFeature->GetFieldAsStringList(iField);
     991           3 :                 while (papszIter != nullptr && *papszIter != nullptr)
     992             :                 {
     993           2 :                     char *pszEscaped = OGRGetXML_UTF8_EscapedString(*papszIter);
     994           2 :                     GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
     995             :                                   bRemoveAppPrefix, poFieldDefn, pszEscaped);
     996           2 :                     CPLFree(pszEscaped);
     997             : 
     998           2 :                     papszIter++;
     999             :                 }
    1000             :             }
    1001         499 :             else if (eType == OFTIntegerList)
    1002             :             {
    1003           2 :                 int nCount = 0;
    1004             :                 const int *panVals =
    1005           2 :                     poFeature->GetFieldAsIntegerList(iField, &nCount);
    1006           2 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1007             :                 {
    1008           3 :                     for (int i = 0; i < nCount; i++)
    1009             :                     {
    1010             :                         // 0 and 1 are OK, but the canonical representation is
    1011             :                         // false and true.
    1012           2 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1013             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1014           2 :                                       panVals[i] ? "true" : "false");
    1015             :                     }
    1016             :                 }
    1017             :                 else
    1018             :                 {
    1019           3 :                     for (int i = 0; i < nCount; i++)
    1020             :                     {
    1021           2 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1022             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1023           2 :                                       CPLSPrintf("%d", panVals[i]));
    1024             :                     }
    1025             :                 }
    1026             :             }
    1027         497 :             else if (eType == OFTInteger64List)
    1028             :             {
    1029           2 :                 int nCount = 0;
    1030             :                 const GIntBig *panVals =
    1031           2 :                     poFeature->GetFieldAsInteger64List(iField, &nCount);
    1032           2 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1033             :                 {
    1034           0 :                     for (int i = 0; i < nCount; i++)
    1035             :                     {
    1036             :                         // 0 and 1 are OK, but the canonical representation is
    1037             :                         // false and true.
    1038           0 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1039             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1040           0 :                                       panVals[i] ? "true" : "false");
    1041             :                     }
    1042             :                 }
    1043             :                 else
    1044             :                 {
    1045           5 :                     for (int i = 0; i < nCount; i++)
    1046             :                     {
    1047           3 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1048             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1049           3 :                                       CPLSPrintf(CPL_FRMT_GIB, panVals[i]));
    1050             :                     }
    1051             :                 }
    1052             :             }
    1053         495 :             else if (eType == OFTRealList)
    1054             :             {
    1055           1 :                 int nCount = 0;
    1056             :                 const double *padfVals =
    1057           1 :                     poFeature->GetFieldAsDoubleList(iField, &nCount);
    1058           3 :                 for (int i = 0; i < nCount; i++)
    1059             :                 {
    1060           2 :                     char szBuffer[80] = {};
    1061           2 :                     CPLsnprintf(szBuffer, sizeof(szBuffer), "%.15g",
    1062           2 :                                 padfVals[i]);
    1063           2 :                     GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1064             :                                   bRemoveAppPrefix, poFieldDefn, szBuffer);
    1065             :                 }
    1066             :             }
    1067         625 :             else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1068         131 :                      poFieldDefn->GetSubType() == OFSTBoolean)
    1069             :             {
    1070             :                 // 0 and 1 are OK, but the canonical representation is false and
    1071             :                 // true.
    1072           2 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1073             :                               bRemoveAppPrefix, poFieldDefn,
    1074           2 :                               (poFeature->GetFieldAsInteger(iField)) ? "true"
    1075             :                                                                      : "false");
    1076             :             }
    1077         492 :             else if (eType == OFTDate)
    1078             :             {
    1079          49 :                 const OGRField *poField = poFeature->GetRawFieldRef(iField);
    1080             :                 const char *pszXML =
    1081          98 :                     CPLSPrintf("%04d-%02d-%02d", poField->Date.Year,
    1082          49 :                                poField->Date.Month, poField->Date.Day);
    1083          49 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1084             :                               bRemoveAppPrefix, poFieldDefn, pszXML);
    1085             :             }
    1086         443 :             else if (eType == OFTDateTime)
    1087             :             {
    1088             :                 char *pszXML =
    1089          49 :                     OGRGetXMLDateTime(poFeature->GetRawFieldRef(iField));
    1090          49 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1091             :                               bRemoveAppPrefix, poFieldDefn, pszXML);
    1092          49 :                 CPLFree(pszXML);
    1093             :             }
    1094             :             else
    1095             :             {
    1096         394 :                 const char *pszRaw = poFeature->GetFieldAsString(iField);
    1097             : 
    1098         394 :                 char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
    1099             : 
    1100         394 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1101             :                               bRemoveAppPrefix, poFieldDefn, pszEscaped);
    1102         394 :                 CPLFree(pszEscaped);
    1103             :             }
    1104             :         }
    1105             :     }
    1106             : 
    1107         264 :     if (bWriteSpaceIndentation)
    1108         264 :         VSIFPrintfL(fp, "    ");
    1109         264 :     if (bRemoveAppPrefix)
    1110          20 :         poDS->PrintLine(fp, "</%s>", poFeatureDefn->GetName());
    1111             :     else
    1112         244 :         poDS->PrintLine(fp, "</%s:%s>", pszPrefix, poFeatureDefn->GetName());
    1113         264 :     if (bWriteSpaceIndentation)
    1114         264 :         VSIFPrintfL(fp, "  ");
    1115         264 :     if (bIsGML3Output && !bGMLFeatureCollection)
    1116             :     {
    1117         220 :         if (bRemoveAppPrefix)
    1118          10 :             poDS->PrintLine(fp, "</featureMember>");
    1119             :         else
    1120         210 :             poDS->PrintLine(fp, "</%s:featureMember>", pszPrefix);
    1121             :     }
    1122             :     else
    1123             :     {
    1124          44 :         poDS->PrintLine(fp, "</gml:featureMember>");
    1125             :     }
    1126             : 
    1127         264 :     return !poDS->HasWriteError() ? OGRERR_NONE : OGRERR_FAILURE;
    1128             : }
    1129             : 
    1130             : /************************************************************************/
    1131             : /*                           TestCapability()                           */
    1132             : /************************************************************************/
    1133             : 
    1134         342 : int OGRGMLLayer::TestCapability(const char *pszCap) const
    1135             : 
    1136             : {
    1137         342 :     if (EQUAL(pszCap, OLCSequentialWrite))
    1138          19 :         return bWriter;
    1139             : 
    1140         323 :     else if (EQUAL(pszCap, OLCCreateField))
    1141          18 :         return bWriter && m_iNextGMLId == 0;
    1142             : 
    1143         305 :     else if (EQUAL(pszCap, OLCCreateGeomField))
    1144           4 :         return bWriter && m_iNextGMLId == 0;
    1145             : 
    1146         301 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    1147             :     {
    1148           4 :         if (poFClass == nullptr)
    1149           0 :             return FALSE;
    1150             : 
    1151           4 :         double dfXMin = 0.0;
    1152           4 :         double dfXMax = 0.0;
    1153           4 :         double dfYMin = 0.0;
    1154           4 :         double dfYMax = 0.0;
    1155             : 
    1156           4 :         return poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax);
    1157             :     }
    1158             : 
    1159         297 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    1160             :     {
    1161           1 :         if (poFClass == nullptr || m_poFilterGeom != nullptr ||
    1162           1 :             m_poAttrQuery != nullptr)
    1163           0 :             return FALSE;
    1164             : 
    1165           1 :         return poFClass->GetFeatureCount() != -1;
    1166             :     }
    1167             : 
    1168         296 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1169          15 :         return TRUE;
    1170             : 
    1171         281 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    1172         146 :         return poDS->IsGML3Output();
    1173             : 
    1174         135 :     else if (EQUAL(pszCap, OLCZGeometries))
    1175           3 :         return TRUE;
    1176             : 
    1177             :     else
    1178         132 :         return FALSE;
    1179             : }
    1180             : 
    1181             : /************************************************************************/
    1182             : /*                            CreateField()                             */
    1183             : /************************************************************************/
    1184             : 
    1185         181 : OGRErr OGRGMLLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
    1186             : 
    1187             : {
    1188         181 :     if (!bWriter || m_iNextGMLId != 0)
    1189           0 :         return OGRERR_FAILURE;
    1190             : 
    1191             :     /* -------------------------------------------------------------------- */
    1192             :     /*      Enforce XML naming semantics on element name.                   */
    1193             :     /* -------------------------------------------------------------------- */
    1194         362 :     OGRFieldDefn oCleanCopy(poField);
    1195         181 :     char *pszName = CPLStrdup(poField->GetNameRef());
    1196         181 :     CPLCleanXMLElementName(pszName);
    1197             : 
    1198         181 :     if (strcmp(pszName, poField->GetNameRef()) != 0)
    1199             :     {
    1200           0 :         if (!bApproxOK)
    1201             :         {
    1202           0 :             CPLFree(pszName);
    1203           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1204             :                      "Unable to create field with name '%s', it would not\n"
    1205             :                      "be valid as an XML element name.",
    1206             :                      poField->GetNameRef());
    1207           0 :             return OGRERR_FAILURE;
    1208             :         }
    1209             : 
    1210           0 :         oCleanCopy.SetName(pszName);
    1211           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1212             :                  "Field name '%s' adjusted to '%s' to be a valid\n"
    1213             :                  "XML element name.",
    1214             :                  poField->GetNameRef(), pszName);
    1215             :     }
    1216             : 
    1217         181 :     CPLFree(pszName);
    1218             : 
    1219         181 :     poFeatureDefn->AddFieldDefn(&oCleanCopy);
    1220             : 
    1221         181 :     return OGRERR_NONE;
    1222             : }
    1223             : 
    1224             : /************************************************************************/
    1225             : /*                          CreateGeomField()                           */
    1226             : /************************************************************************/
    1227             : 
    1228          19 : OGRErr OGRGMLLayer::CreateGeomField(const OGRGeomFieldDefn *poField,
    1229             :                                     int bApproxOK)
    1230             : 
    1231             : {
    1232          19 :     if (!bWriter || m_iNextGMLId != 0)
    1233           0 :         return OGRERR_FAILURE;
    1234             : 
    1235             :     /* -------------------------------------------------------------------- */
    1236             :     /*      Enforce XML naming semantics on element name.                   */
    1237             :     /* -------------------------------------------------------------------- */
    1238          38 :     OGRGeomFieldDefn oCleanCopy(poField);
    1239          19 :     const auto poSRSOri = poField->GetSpatialRef();
    1240          19 :     poDS->DeclareNewWriteSRS(poSRSOri);
    1241          19 :     if (poSRSOri)
    1242             :     {
    1243           6 :         auto poSRS = poSRSOri->Clone();
    1244           6 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1245           6 :         oCleanCopy.SetSpatialRef(poSRS);
    1246           6 :         poSRS->Release();
    1247             :     }
    1248          19 :     char *pszName = CPLStrdup(poField->GetNameRef());
    1249          19 :     CPLCleanXMLElementName(pszName);
    1250             : 
    1251          19 :     if (strcmp(pszName, poField->GetNameRef()) != 0)
    1252             :     {
    1253           0 :         if (!bApproxOK)
    1254             :         {
    1255           0 :             CPLFree(pszName);
    1256           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1257             :                      "Unable to create field with name '%s', it would not\n"
    1258             :                      "be valid as an XML element name.",
    1259             :                      poField->GetNameRef());
    1260           0 :             return OGRERR_FAILURE;
    1261             :         }
    1262             : 
    1263           0 :         oCleanCopy.SetName(pszName);
    1264           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1265             :                  "Field name '%s' adjusted to '%s' to be a valid\n"
    1266             :                  "XML element name.",
    1267             :                  poField->GetNameRef(), pszName);
    1268             :     }
    1269             : 
    1270          19 :     CPLFree(pszName);
    1271             : 
    1272          19 :     poFeatureDefn->AddGeomFieldDefn(&oCleanCopy);
    1273             : 
    1274          19 :     return OGRERR_NONE;
    1275             : }
    1276             : 
    1277             : /************************************************************************/
    1278             : /*                             GetDataset()                             */
    1279             : /************************************************************************/
    1280             : 
    1281          20 : GDALDataset *OGRGMLLayer::GetDataset()
    1282             : {
    1283          20 :     return poDS;
    1284             : }

Generated by: LCOV version 1.14