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

Generated by: LCOV version 1.14