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

Generated by: LCOV version 1.14