LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - ogrgmllayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 537 596 90.1 %
Date: 2025-11-29 13:55:01 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         163 :                         papoGeometries[i] = OGRGeometryFactory::forceTo(
     332             :                             poGeom,
     333         163 :                             poFeatureDefn->GetGeomFieldDefn(i)->GetType());
     334         163 :                         poGeom = nullptr;
     335             :                     }
     336             :                     else
     337             :                     {
     338             :                         // We assume the createFromGML() function would have
     339             :                         // already reported the error.
     340           0 :                         for (int j = 0; j < poFeatureDefn->GetGeomFieldCount();
     341             :                              j++)
     342             :                         {
     343           0 :                             delete papoGeometries[j];
     344             :                         }
     345           0 :                         CPLFree(papoGeometries);
     346           0 :                         delete poGMLFeature;
     347           0 :                         return nullptr;
     348             :                     }
     349             :                 }
     350             :             }
     351             : 
     352           0 :             if (m_poFilterGeom != nullptr && m_iGeomFieldFilter >= 0 &&
     353           0 :                 m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount() &&
     354          94 :                 papoGeometries[m_iGeomFieldFilter] &&
     355           0 :                 !FilterGeometry(papoGeometries[m_iGeomFieldFilter]))
     356             :             {
     357           0 :                 for (int j = 0; j < poFeatureDefn->GetGeomFieldCount(); j++)
     358             :                 {
     359           0 :                     delete papoGeometries[j];
     360             :                 }
     361           0 :                 CPLFree(papoGeometries);
     362           0 :                 delete poGMLFeature;
     363           0 :                 continue;
     364             :             }
     365             :         }
     366         675 :         else if (papsGeometry[0] &&
     367         559 :                  strcmp(papsGeometry[0]->pszValue, "null") == 0)
     368             :         {
     369             :             // do nothing
     370             :         }
     371         671 :         else if (papsGeometry[0] != nullptr)
     372             :         {
     373         555 :             const char *pszSRSName = poDS->GetGlobalSRSName();
     374         555 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     375         555 :             poGeom = GML_BuildOGRGeometryFromList(
     376         555 :                 papsGeometry, true, poDS->GetInvertAxisOrderIfLatLong(),
     377         555 :                 pszSRSName, poDS->GetConsiderEPSGAsURN(),
     378         555 :                 poDS->GetSwapCoordinates(), poDS->GetSecondaryGeometryOption(),
     379         555 :                 m_srsCache.get(), bFaceHoleNegative);
     380         555 :             CPLPopErrorHandler();
     381             : 
     382             :             // Do geometry type changes if needed to match layer geometry type.
     383         555 :             if (poGeom != nullptr)
     384             :             {
     385         551 :                 poGeom = OGRGeometryFactory::forceTo(poGeom, GetGeomType());
     386             :             }
     387             :             else
     388             :             {
     389           4 :                 const CPLString osLastErrorMsg(CPLGetLastErrorMsg());
     390             : 
     391           8 :                 CPLError(
     392             :                     bSkipCorruptedFeatures ? CE_Warning : CE_Failure,
     393             :                     CPLE_AppDefined,
     394             :                     "Geometry of feature " CPL_FRMT_GIB
     395             :                     " %scannot be parsed: %s%s",
     396           4 :                     nFID, pszGML_FID ? CPLSPrintf("%s ", pszGML_FID) : "",
     397             :                     osLastErrorMsg.c_str(),
     398             :                     bSkipCorruptedFeatures
     399             :                         ? ". Skipping to next feature."
     400             :                         : ". You may set the GML_SKIP_CORRUPTED_FEATURES "
     401             :                           "configuration option to YES to skip to the next "
     402             :                           "feature");
     403           4 :                 delete poGMLFeature;
     404           4 :                 if (bSkipCorruptedFeatures)
     405           2 :                     continue;
     406           2 :                 return nullptr;
     407             :             }
     408             : 
     409         551 :             if (m_poFilterGeom != nullptr && !FilterGeometry(poGeom))
     410             :             {
     411          20 :                 delete poGMLFeature;
     412          20 :                 delete poGeom;
     413          20 :                 continue;
     414             :             }
     415             :         }
     416             : 
     417             :         /* --------------------------------------------------------------------
     418             :          */
     419             :         /*      Convert the whole feature into an OGRFeature. */
     420             :         /* --------------------------------------------------------------------
     421             :          */
     422         745 :         int iDstField = 0;
     423         745 :         OGRFeature *poOGRFeature = new OGRFeature(poFeatureDefn);
     424             : 
     425         745 :         poOGRFeature->SetFID(nFID);
     426         745 :         if (poDS->ExposeId())
     427             :         {
     428         637 :             if (pszGML_FID)
     429         594 :                 poOGRFeature->SetField(iDstField, pszGML_FID);
     430         637 :             iDstField++;
     431             :         }
     432             : 
     433         745 :         const int nPropertyCount = poFClass->GetPropertyCount();
     434        3528 :         for (int iField = 0; iField < nPropertyCount; iField++, iDstField++)
     435             :         {
     436             :             const GMLProperty *psGMLProperty =
     437        2783 :                 poGMLFeature->GetProperty(iField);
     438        2783 :             if (psGMLProperty == nullptr || psGMLProperty->nSubProperties == 0)
     439         616 :                 continue;
     440             : 
     441        2167 :             if (EQUAL(psGMLProperty->papszSubProperties[0], OGR_GML_NULL))
     442             :             {
     443           8 :                 poOGRFeature->SetFieldNull(iDstField);
     444           8 :                 continue;
     445             :             }
     446             : 
     447        2159 :             switch (poFClass->GetProperty(iField)->GetType())
     448             :             {
     449         361 :                 case GMLPT_Real:
     450             :                 {
     451         361 :                     poOGRFeature->SetField(
     452             :                         iDstField,
     453         361 :                         CPLAtof(psGMLProperty->papszSubProperties[0]));
     454             :                 }
     455         361 :                 break;
     456             : 
     457          13 :                 case GMLPT_IntegerList:
     458             :                 {
     459          13 :                     const int nCount = psGMLProperty->nSubProperties;
     460             :                     int *panIntList =
     461          13 :                         static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
     462             : 
     463          39 :                     for (int i = 0; i < nCount; i++)
     464          26 :                         panIntList[i] =
     465          26 :                             atoi(psGMLProperty->papszSubProperties[i]);
     466             : 
     467          13 :                     poOGRFeature->SetField(iDstField, nCount, panIntList);
     468          13 :                     CPLFree(panIntList);
     469             :                 }
     470          13 :                 break;
     471             : 
     472           6 :                 case GMLPT_Integer64List:
     473             :                 {
     474           6 :                     const int nCount = psGMLProperty->nSubProperties;
     475             :                     GIntBig *panIntList = static_cast<GIntBig *>(
     476           6 :                         CPLMalloc(sizeof(GIntBig) * nCount));
     477             : 
     478          15 :                     for (int i = 0; i < nCount; i++)
     479          18 :                         panIntList[i] =
     480           9 :                             CPLAtoGIntBig(psGMLProperty->papszSubProperties[i]);
     481             : 
     482           6 :                     poOGRFeature->SetField(iDstField, nCount, panIntList);
     483           6 :                     CPLFree(panIntList);
     484             :                 }
     485           6 :                 break;
     486             : 
     487          25 :                 case GMLPT_RealList:
     488             :                 {
     489          25 :                     const int nCount = psGMLProperty->nSubProperties;
     490             :                     double *padfList = static_cast<double *>(
     491          25 :                         CPLMalloc(sizeof(double) * nCount));
     492             : 
     493          59 :                     for (int i = 0; i < nCount; i++)
     494          68 :                         padfList[i] =
     495          34 :                             CPLAtof(psGMLProperty->papszSubProperties[i]);
     496             : 
     497          25 :                     poOGRFeature->SetField(iDstField, nCount, padfList);
     498          25 :                     CPLFree(padfList);
     499             :                 }
     500          25 :                 break;
     501             : 
     502         106 :                 case GMLPT_StringList:
     503             :                 case GMLPT_FeaturePropertyList:
     504             :                 {
     505         106 :                     poOGRFeature->SetField(iDstField,
     506         106 :                                            psGMLProperty->papszSubProperties);
     507             :                 }
     508         106 :                 break;
     509             : 
     510          46 :                 case GMLPT_Boolean:
     511             :                 {
     512          46 :                     if (strcmp(psGMLProperty->papszSubProperties[0], "true") ==
     513           7 :                             0 ||
     514           7 :                         strcmp(psGMLProperty->papszSubProperties[0], "1") == 0)
     515             :                     {
     516          39 :                         poOGRFeature->SetField(iDstField, 1);
     517             :                     }
     518           7 :                     else if (strcmp(psGMLProperty->papszSubProperties[0],
     519           0 :                                     "false") == 0 ||
     520           0 :                              strcmp(psGMLProperty->papszSubProperties[0],
     521             :                                     "0") == 0)
     522             :                     {
     523           7 :                         poOGRFeature->SetField(iDstField, 0);
     524             :                     }
     525             :                     else
     526             :                     {
     527           0 :                         poOGRFeature->SetField(
     528           0 :                             iDstField, psGMLProperty->papszSubProperties[0]);
     529             :                     }
     530          46 :                     break;
     531             :                 }
     532             : 
     533           3 :                 case GMLPT_BooleanList:
     534             :                 {
     535           3 :                     const int nCount = psGMLProperty->nSubProperties;
     536             :                     int *panIntList =
     537           3 :                         static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
     538             : 
     539           9 :                     for (int i = 0; i < nCount; i++)
     540             :                     {
     541           6 :                         panIntList[i] =
     542           6 :                             (strcmp(psGMLProperty->papszSubProperties[i],
     543           9 :                                     "true") == 0 ||
     544           3 :                              strcmp(psGMLProperty->papszSubProperties[i],
     545             :                                     "1") == 0);
     546             :                     }
     547             : 
     548           3 :                     poOGRFeature->SetField(iDstField, nCount, panIntList);
     549           3 :                     CPLFree(panIntList);
     550           3 :                     break;
     551             :                 }
     552             : 
     553        1599 :                 default:
     554        1599 :                     poOGRFeature->SetField(
     555        1599 :                         iDstField, psGMLProperty->papszSubProperties[0]);
     556        1599 :                     break;
     557             :             }
     558             :         }
     559             : 
     560         745 :         delete poGMLFeature;
     561         745 :         poGMLFeature = nullptr;
     562             : 
     563             :         // Assign the geometry before the attribute filter because
     564             :         // the attribute filter may use a special field like OGR_GEOMETRY.
     565         745 :         if (papoGeometries != nullptr)
     566             :         {
     567         301 :             for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     568             :             {
     569         207 :                 poOGRFeature->SetGeomFieldDirectly(i, papoGeometries[i]);
     570             :             }
     571          94 :             CPLFree(papoGeometries);
     572          94 :             papoGeometries = nullptr;
     573             :         }
     574             :         else
     575             :         {
     576         651 :             poOGRFeature->SetGeometryDirectly(poGeom);
     577             :         }
     578             : 
     579             :         // Assign SRS.
     580        1526 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     581             :         {
     582         781 :             poGeom = poOGRFeature->GetGeomFieldRef(i);
     583         781 :             if (poGeom != nullptr)
     584             :             {
     585             :                 const OGRSpatialReference *poSRS =
     586         694 :                     poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef();
     587         694 :                 if (poSRS != nullptr)
     588         147 :                     poGeom->assignSpatialReference(poSRS);
     589             :             }
     590             :         }
     591             : 
     592             :         /* --------------------------------------------------------------------
     593             :          */
     594             :         /*      Test against the attribute query. */
     595             :         /* --------------------------------------------------------------------
     596             :          */
     597         745 :         if (m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poOGRFeature))
     598             :         {
     599          28 :             delete poOGRFeature;
     600          28 :             continue;
     601             :         }
     602             : 
     603             :         // Got the desired feature.
     604         717 :         return poOGRFeature;
     605         874 :     }
     606             : }
     607             : 
     608             : /************************************************************************/
     609             : /*                          GetFeatureCount()                           */
     610             : /************************************************************************/
     611             : 
     612          62 : GIntBig OGRGMLLayer::GetFeatureCount(int bForce)
     613             : 
     614             : {
     615          62 :     if (poFClass == nullptr)
     616           0 :         return 0;
     617             : 
     618          62 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     619           6 :         return OGRLayer::GetFeatureCount(bForce);
     620             : 
     621             :     // If the schema is read from a .xsd file, we haven't read
     622             :     // the feature count, so compute it now.
     623          56 :     GIntBig nFeatureCount = poFClass->GetFeatureCount();
     624          56 :     if (nFeatureCount < 0)
     625             :     {
     626          26 :         nFeatureCount = OGRLayer::GetFeatureCount(bForce);
     627          26 :         poFClass->SetFeatureCount(nFeatureCount);
     628             :     }
     629             : 
     630          56 :     return nFeatureCount;
     631             : }
     632             : 
     633             : /************************************************************************/
     634             : /*                            IGetExtent()                              */
     635             : /************************************************************************/
     636             : 
     637          10 : OGRErr OGRGMLLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
     638             :                                bool bForce)
     639             : 
     640             : {
     641          10 :     if (GetGeomType() == wkbNone)
     642           0 :         return OGRERR_FAILURE;
     643             : 
     644          10 :     double dfXMin = 0.0;
     645          10 :     double dfXMax = 0.0;
     646          10 :     double dfYMin = 0.0;
     647          10 :     double dfYMax = 0.0;
     648          20 :     if (poFClass != nullptr &&
     649          10 :         poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
     650             :     {
     651           8 :         psExtent->MinX = dfXMin;
     652           8 :         psExtent->MaxX = dfXMax;
     653           8 :         psExtent->MinY = dfYMin;
     654           8 :         psExtent->MaxY = dfYMax;
     655             : 
     656           8 :         return OGRERR_NONE;
     657             :     }
     658             : 
     659           2 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     660             : }
     661             : 
     662             : /************************************************************************/
     663             : /*                             GetExtent()                              */
     664             : /************************************************************************/
     665             : 
     666         505 : static void GMLWriteField(OGRGMLDataSource *poDS, VSILFILE *fp,
     667             :                           bool bWriteSpaceIndentation, const char *pszPrefix,
     668             :                           bool bRemoveAppPrefix, OGRFieldDefn *poFieldDefn,
     669             :                           const char *pszVal)
     670             : 
     671             : {
     672         505 :     const char *pszFieldName = poFieldDefn->GetNameRef();
     673             : 
     674         505 :     while (*pszVal == ' ')
     675           0 :         pszVal++;
     676             : 
     677         505 :     if (bWriteSpaceIndentation)
     678         505 :         VSIFPrintfL(fp, "      ");
     679             : 
     680         505 :     if (bRemoveAppPrefix)
     681          60 :         poDS->PrintLine(fp, "<%s>%s</%s>", pszFieldName, pszVal, pszFieldName);
     682             :     else
     683         445 :         poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix, pszFieldName,
     684             :                         pszVal, pszPrefix, pszFieldName);
     685         505 : }
     686             : 
     687             : /************************************************************************/
     688             : /*                           ICreateFeature()                            */
     689             : /************************************************************************/
     690             : 
     691         266 : OGRErr OGRGMLLayer::ICreateFeature(OGRFeature *poFeature)
     692             : 
     693             : {
     694         266 :     const bool bIsGML3Output = poDS->IsGML3Output();
     695         266 :     VSILFILE *fp = poDS->GetOutputFP();
     696         266 :     const bool bWriteSpaceIndentation = poDS->WriteSpaceIndentation();
     697         266 :     const char *pszPrefix = poDS->GetAppPrefix();
     698         266 :     const bool bRemoveAppPrefix = poDS->RemoveAppPrefix();
     699         266 :     const bool bGMLFeatureCollection = poDS->GMLFeatureCollection();
     700             : 
     701         266 :     if (!bWriter || poDS->HasWriteError())
     702           0 :         return OGRERR_FAILURE;
     703             : 
     704         266 :     poFeature->FillUnsetWithDefault(TRUE, nullptr);
     705         266 :     if (!poFeature->Validate(OGR_F_VAL_ALL & ~OGR_F_VAL_GEOM_TYPE &
     706             :                                  ~OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT,
     707             :                              TRUE))
     708           2 :         return OGRERR_FAILURE;
     709             : 
     710         264 :     if (bWriteSpaceIndentation)
     711         264 :         VSIFPrintfL(fp, "  ");
     712         264 :     if (bIsGML3Output && !bGMLFeatureCollection)
     713             :     {
     714         220 :         if (bRemoveAppPrefix)
     715          10 :             poDS->PrintLine(fp, "<featureMember>");
     716             :         else
     717         210 :             poDS->PrintLine(fp, "<%s:featureMember>", pszPrefix);
     718             :     }
     719             :     else
     720             :     {
     721          44 :         poDS->PrintLine(fp, "<gml:featureMember>");
     722             :     }
     723             : 
     724         264 :     if (poFeature->GetFID() == OGRNullFID)
     725         251 :         poFeature->SetFID(m_iNextGMLId++);
     726             : 
     727         264 :     if (bWriteSpaceIndentation)
     728         264 :         VSIFPrintfL(fp, "    ");
     729         264 :     VSIFPrintfL(fp, "<");
     730         264 :     if (!bRemoveAppPrefix)
     731         244 :         VSIFPrintfL(fp, "%s:", pszPrefix);
     732             : 
     733         264 :     int nGMLIdIndex = -1;
     734         264 :     if (bIsGML3Output)
     735             :     {
     736         230 :         nGMLIdIndex = poFeatureDefn->GetFieldIndex("gml_id");
     737         230 :         if (nGMLIdIndex >= 0 && poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
     738           3 :             poDS->PrintLine(fp, "%s gml:id=\"%s\">", poFeatureDefn->GetName(),
     739             :                             poFeature->GetFieldAsString(nGMLIdIndex));
     740             :         else
     741         454 :             poDS->PrintLine(fp, "%s gml:id=\"%s." CPL_FRMT_GIB "\">",
     742         227 :                             poFeatureDefn->GetName(), poFeatureDefn->GetName(),
     743             :                             poFeature->GetFID());
     744             :     }
     745             :     else
     746             :     {
     747          34 :         nGMLIdIndex = poFeatureDefn->GetFieldIndex("fid");
     748          34 :         if (bUseOldFIDFormat)
     749             :         {
     750           0 :             poDS->PrintLine(fp, "%s fid=\"F" CPL_FRMT_GIB "\">",
     751           0 :                             poFeatureDefn->GetName(), poFeature->GetFID());
     752             :         }
     753          44 :         else if (nGMLIdIndex >= 0 &&
     754          10 :                  poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
     755             :         {
     756          10 :             poDS->PrintLine(fp, "%s fid=\"%s\">", poFeatureDefn->GetName(),
     757             :                             poFeature->GetFieldAsString(nGMLIdIndex));
     758             :         }
     759             :         else
     760             :         {
     761          48 :             poDS->PrintLine(fp, "%s fid=\"%s." CPL_FRMT_GIB "\">",
     762          24 :                             poFeatureDefn->GetName(), poFeatureDefn->GetName(),
     763             :                             poFeature->GetFID());
     764             :         }
     765             :     }
     766             : 
     767         517 :     for (int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount();
     768             :          iGeomField++)
     769             :     {
     770             :         const OGRGeomFieldDefn *poFieldDefn =
     771         253 :             poFeatureDefn->GetGeomFieldDefn(iGeomField);
     772             : 
     773             :         // Write out Geometry - for now it isn't indented properly.
     774             :         // GML geometries don't like very much the concept of empty geometry.
     775         253 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField);
     776         253 :         if (poGeom != nullptr && !poGeom->IsEmpty())
     777             :         {
     778         215 :             OGREnvelope3D sGeomBounds;
     779             : 
     780         215 :             const int nCoordDimension = poGeom->getCoordinateDimension();
     781             : 
     782         215 :             poGeom->getEnvelope(&sGeomBounds);
     783         215 :             if (poDS->HasWriteGlobalSRS())
     784         211 :                 poDS->GrowExtents(&sGeomBounds, nCoordDimension);
     785             : 
     786         356 :             if (poGeom->getSpatialReference() == nullptr &&
     787         141 :                 poFieldDefn->GetSpatialRef() != nullptr)
     788          18 :                 poGeom->assignSpatialReference(poFieldDefn->GetSpatialRef());
     789             : 
     790         215 :             const auto &oCoordPrec = poFieldDefn->GetCoordinatePrecision();
     791             : 
     792         215 :             if (bIsGML3Output && poDS->WriteFeatureBoundedBy())
     793             :             {
     794         171 :                 bool bCoordSwap = false;
     795             : 
     796             :                 char *pszSRSName =
     797         171 :                     GML_GetSRSName(poGeom->getSpatialReference(),
     798         171 :                                    poDS->GetSRSNameFormat(), &bCoordSwap);
     799         171 :                 char szLowerCorner[75] = {};
     800         171 :                 char szUpperCorner[75] = {};
     801             : 
     802         171 :                 OGRWktOptions coordOpts;
     803             : 
     804         171 :                 if (oCoordPrec.dfXYResolution !=
     805             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     806             :                 {
     807           3 :                     coordOpts.format = OGRWktFormat::F;
     808           3 :                     coordOpts.xyPrecision =
     809           3 :                         OGRGeomCoordinatePrecision::ResolutionToPrecision(
     810           3 :                             oCoordPrec.dfXYResolution);
     811             :                 }
     812         171 :                 if (oCoordPrec.dfZResolution !=
     813             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     814             :                 {
     815           3 :                     coordOpts.format = OGRWktFormat::F;
     816           3 :                     coordOpts.zPrecision =
     817           3 :                         OGRGeomCoordinatePrecision::ResolutionToPrecision(
     818           3 :                             oCoordPrec.dfZResolution);
     819             :                 }
     820             : 
     821         342 :                 std::string wkt;
     822         171 :                 if (bCoordSwap)
     823             :                 {
     824          36 :                     wkt = OGRMakeWktCoordinate(
     825             :                         sGeomBounds.MinY, sGeomBounds.MinX, sGeomBounds.MinZ,
     826          18 :                         nCoordDimension, coordOpts);
     827          18 :                     memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
     828             : 
     829          36 :                     wkt = OGRMakeWktCoordinate(
     830             :                         sGeomBounds.MaxY, sGeomBounds.MaxX, sGeomBounds.MaxZ,
     831          18 :                         nCoordDimension, coordOpts);
     832          18 :                     memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
     833             :                 }
     834             :                 else
     835             :                 {
     836         306 :                     wkt = OGRMakeWktCoordinate(
     837             :                         sGeomBounds.MinX, sGeomBounds.MinY, sGeomBounds.MinZ,
     838         153 :                         nCoordDimension, coordOpts);
     839         153 :                     memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
     840             : 
     841         306 :                     wkt = OGRMakeWktCoordinate(
     842             :                         sGeomBounds.MaxX, sGeomBounds.MaxY, sGeomBounds.MaxZ,
     843         153 :                         nCoordDimension, coordOpts);
     844         153 :                     memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
     845             :                 }
     846         171 :                 if (bWriteSpaceIndentation)
     847         171 :                     VSIFPrintfL(fp, "      ");
     848         171 :                 poDS->PrintLine(
     849             :                     fp,
     850             :                     "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s"
     851             :                     "</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner>"
     852             :                     "</gml:Envelope></gml:boundedBy>",
     853             :                     (nCoordDimension == 3) ? " srsDimension=\"3\"" : "",
     854             :                     pszSRSName, szLowerCorner, szUpperCorner);
     855         171 :                 CPLFree(pszSRSName);
     856             :             }
     857             : 
     858         215 :             char **papszOptions = nullptr;
     859         215 :             if (bIsGML3Output)
     860             :             {
     861         181 :                 papszOptions = CSLAddString(papszOptions, "FORMAT=GML3");
     862         181 :                 if (poDS->GetSRSNameFormat() == SRSNAME_SHORT)
     863             :                     papszOptions =
     864           1 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=SHORT");
     865         180 :                 else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URN)
     866             :                     papszOptions =
     867         167 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URN");
     868          13 :                 else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URL)
     869             :                     papszOptions =
     870          13 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URL");
     871             :             }
     872         215 :             const char *pszSRSDimensionLoc = poDS->GetSRSDimensionLoc();
     873         215 :             if (pszSRSDimensionLoc != nullptr)
     874           3 :                 papszOptions = CSLSetNameValue(papszOptions, "SRSDIMENSION_LOC",
     875             :                                                pszSRSDimensionLoc);
     876         215 :             if (poDS->IsGML32Output())
     877             :             {
     878         115 :                 if (poFeatureDefn->GetGeomFieldCount() > 1)
     879          18 :                     papszOptions = CSLAddString(
     880             :                         papszOptions, CPLSPrintf("GMLID=%s.%s." CPL_FRMT_GIB,
     881           9 :                                                  poFeatureDefn->GetName(),
     882             :                                                  poFieldDefn->GetNameRef(),
     883             :                                                  poFeature->GetFID()));
     884             :                 else
     885         212 :                     papszOptions = CSLAddString(
     886             :                         papszOptions, CPLSPrintf("GMLID=%s.geom." CPL_FRMT_GIB,
     887         106 :                                                  poFeatureDefn->GetName(),
     888             :                                                  poFeature->GetFID()));
     889             :             }
     890             : 
     891         215 :             if (oCoordPrec.dfXYResolution !=
     892             :                 OGRGeomCoordinatePrecision::UNKNOWN)
     893             :             {
     894           3 :                 papszOptions = CSLAddString(
     895             :                     papszOptions, CPLSPrintf("XY_COORD_RESOLUTION=%g",
     896           3 :                                              oCoordPrec.dfXYResolution));
     897             :             }
     898         215 :             if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     899             :             {
     900           3 :                 papszOptions = CSLAddString(
     901             :                     papszOptions, CPLSPrintf("Z_COORD_RESOLUTION=%g",
     902           3 :                                              oCoordPrec.dfZResolution));
     903             :             }
     904             : 
     905         215 :             char *pszGeometry = nullptr;
     906         215 :             if (!bIsGML3Output && OGR_GT_IsNonLinear(poGeom->getGeometryType()))
     907             :             {
     908           0 :                 OGRGeometry *poGeomTmp = OGRGeometryFactory::forceTo(
     909           0 :                     poGeom->clone(),
     910           0 :                     OGR_GT_GetLinear(poGeom->getGeometryType()));
     911           0 :                 pszGeometry = poGeomTmp->exportToGML(papszOptions);
     912           0 :                 delete poGeomTmp;
     913             :             }
     914             :             else
     915             :             {
     916         215 :                 if (wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
     917             :                 {
     918           0 :                     pszGeometry = poGeom->exportToGML(papszOptions);
     919             : 
     920             :                     const char *pszGMLID =
     921           0 :                         poDS->IsGML32Output()
     922           0 :                             ? CPLSPrintf(
     923             :                                   " gml:id=\"%s\"",
     924             :                                   CSLFetchNameValue(papszOptions, "GMLID"))
     925           0 :                             : "";
     926           0 :                     char *pszNewGeom = CPLStrdup(
     927             :                         CPLSPrintf("<gml:TriangulatedSurface%s><gml:patches>%s<"
     928             :                                    "/gml:patches></gml:TriangulatedSurface>",
     929             :                                    pszGMLID, pszGeometry));
     930           0 :                     CPLFree(pszGeometry);
     931           0 :                     pszGeometry = pszNewGeom;
     932             :                 }
     933             :                 else
     934             :                 {
     935         215 :                     pszGeometry = poGeom->exportToGML(papszOptions);
     936             :                 }
     937             :             }
     938         215 :             CSLDestroy(papszOptions);
     939         215 :             if (pszGeometry)
     940             :             {
     941         214 :                 if (bWriteSpaceIndentation)
     942         214 :                     VSIFPrintfL(fp, "      ");
     943         214 :                 if (bRemoveAppPrefix)
     944          20 :                     poDS->PrintLine(fp, "<%s>%s</%s>",
     945             :                                     poFieldDefn->GetNameRef(), pszGeometry,
     946             :                                     poFieldDefn->GetNameRef());
     947             :                 else
     948         194 :                     poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix,
     949             :                                     poFieldDefn->GetNameRef(), pszGeometry,
     950             :                                     pszPrefix, poFieldDefn->GetNameRef());
     951             :             }
     952             :             else
     953             :             {
     954           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     955             :                          "Export of geometry to GML failed");
     956             :             }
     957         215 :             CPLFree(pszGeometry);
     958             :         }
     959             :     }
     960             : 
     961             :     // Write all "set" fields.
     962         885 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     963             :     {
     964         621 :         if (iField == nGMLIdIndex)
     965          13 :             continue;
     966         608 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
     967             : 
     968         608 :         if (poFeature->IsFieldNull(iField))
     969             :         {
     970           1 :             const char *pszFieldName = poFieldDefn->GetNameRef();
     971             : 
     972           1 :             if (bWriteSpaceIndentation)
     973           1 :                 VSIFPrintfL(fp, "      ");
     974             : 
     975           1 :             if (bRemoveAppPrefix)
     976           0 :                 poDS->PrintLine(fp, "<%s xsi:nil=\"true\"/>", pszFieldName);
     977             :             else
     978           1 :                 poDS->PrintLine(fp, "<%s:%s xsi:nil=\"true\"/>", pszPrefix,
     979             :                                 pszFieldName);
     980             :         }
     981         607 :         else if (poFeature->IsFieldSet(iField))
     982             :         {
     983         500 :             OGRFieldType eType = poFieldDefn->GetType();
     984         500 :             if (eType == OFTStringList)
     985             :             {
     986           1 :                 char **papszIter = poFeature->GetFieldAsStringList(iField);
     987           3 :                 while (papszIter != nullptr && *papszIter != nullptr)
     988             :                 {
     989           2 :                     char *pszEscaped = OGRGetXML_UTF8_EscapedString(*papszIter);
     990           2 :                     GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
     991             :                                   bRemoveAppPrefix, poFieldDefn, pszEscaped);
     992           2 :                     CPLFree(pszEscaped);
     993             : 
     994           2 :                     papszIter++;
     995             :                 }
     996             :             }
     997         499 :             else if (eType == OFTIntegerList)
     998             :             {
     999           2 :                 int nCount = 0;
    1000             :                 const int *panVals =
    1001           2 :                     poFeature->GetFieldAsIntegerList(iField, &nCount);
    1002           2 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1003             :                 {
    1004           3 :                     for (int i = 0; i < nCount; i++)
    1005             :                     {
    1006             :                         // 0 and 1 are OK, but the canonical representation is
    1007             :                         // false and true.
    1008           2 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1009             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1010           2 :                                       panVals[i] ? "true" : "false");
    1011             :                     }
    1012             :                 }
    1013             :                 else
    1014             :                 {
    1015           3 :                     for (int i = 0; i < nCount; i++)
    1016             :                     {
    1017           2 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1018             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1019           2 :                                       CPLSPrintf("%d", panVals[i]));
    1020             :                     }
    1021             :                 }
    1022             :             }
    1023         497 :             else if (eType == OFTInteger64List)
    1024             :             {
    1025           2 :                 int nCount = 0;
    1026             :                 const GIntBig *panVals =
    1027           2 :                     poFeature->GetFieldAsInteger64List(iField, &nCount);
    1028           2 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1029             :                 {
    1030           0 :                     for (int i = 0; i < nCount; i++)
    1031             :                     {
    1032             :                         // 0 and 1 are OK, but the canonical representation is
    1033             :                         // false and true.
    1034           0 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1035             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1036           0 :                                       panVals[i] ? "true" : "false");
    1037             :                     }
    1038             :                 }
    1039             :                 else
    1040             :                 {
    1041           5 :                     for (int i = 0; i < nCount; i++)
    1042             :                     {
    1043           3 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1044             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1045           3 :                                       CPLSPrintf(CPL_FRMT_GIB, panVals[i]));
    1046             :                     }
    1047             :                 }
    1048             :             }
    1049         495 :             else if (eType == OFTRealList)
    1050             :             {
    1051           1 :                 int nCount = 0;
    1052             :                 const double *padfVals =
    1053           1 :                     poFeature->GetFieldAsDoubleList(iField, &nCount);
    1054           3 :                 for (int i = 0; i < nCount; i++)
    1055             :                 {
    1056           2 :                     char szBuffer[80] = {};
    1057           2 :                     CPLsnprintf(szBuffer, sizeof(szBuffer), "%.15g",
    1058           2 :                                 padfVals[i]);
    1059           2 :                     GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1060             :                                   bRemoveAppPrefix, poFieldDefn, szBuffer);
    1061             :                 }
    1062             :             }
    1063         625 :             else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1064         131 :                      poFieldDefn->GetSubType() == OFSTBoolean)
    1065             :             {
    1066             :                 // 0 and 1 are OK, but the canonical representation is false and
    1067             :                 // true.
    1068           2 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1069             :                               bRemoveAppPrefix, poFieldDefn,
    1070           2 :                               (poFeature->GetFieldAsInteger(iField)) ? "true"
    1071             :                                                                      : "false");
    1072             :             }
    1073         492 :             else if (eType == OFTDate)
    1074             :             {
    1075          49 :                 const OGRField *poField = poFeature->GetRawFieldRef(iField);
    1076             :                 const char *pszXML =
    1077          98 :                     CPLSPrintf("%04d-%02d-%02d", poField->Date.Year,
    1078          49 :                                poField->Date.Month, poField->Date.Day);
    1079          49 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1080             :                               bRemoveAppPrefix, poFieldDefn, pszXML);
    1081             :             }
    1082         443 :             else if (eType == OFTDateTime)
    1083             :             {
    1084             :                 char *pszXML =
    1085          49 :                     OGRGetXMLDateTime(poFeature->GetRawFieldRef(iField));
    1086          49 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1087             :                               bRemoveAppPrefix, poFieldDefn, pszXML);
    1088          49 :                 CPLFree(pszXML);
    1089             :             }
    1090             :             else
    1091             :             {
    1092         394 :                 const char *pszRaw = poFeature->GetFieldAsString(iField);
    1093             : 
    1094         394 :                 char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
    1095             : 
    1096         394 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1097             :                               bRemoveAppPrefix, poFieldDefn, pszEscaped);
    1098         394 :                 CPLFree(pszEscaped);
    1099             :             }
    1100             :         }
    1101             :     }
    1102             : 
    1103         264 :     if (bWriteSpaceIndentation)
    1104         264 :         VSIFPrintfL(fp, "    ");
    1105         264 :     if (bRemoveAppPrefix)
    1106          20 :         poDS->PrintLine(fp, "</%s>", poFeatureDefn->GetName());
    1107             :     else
    1108         244 :         poDS->PrintLine(fp, "</%s:%s>", pszPrefix, poFeatureDefn->GetName());
    1109         264 :     if (bWriteSpaceIndentation)
    1110         264 :         VSIFPrintfL(fp, "  ");
    1111         264 :     if (bIsGML3Output && !bGMLFeatureCollection)
    1112             :     {
    1113         220 :         if (bRemoveAppPrefix)
    1114          10 :             poDS->PrintLine(fp, "</featureMember>");
    1115             :         else
    1116         210 :             poDS->PrintLine(fp, "</%s:featureMember>", pszPrefix);
    1117             :     }
    1118             :     else
    1119             :     {
    1120          44 :         poDS->PrintLine(fp, "</gml:featureMember>");
    1121             :     }
    1122             : 
    1123         264 :     return !poDS->HasWriteError() ? OGRERR_NONE : OGRERR_FAILURE;
    1124             : }
    1125             : 
    1126             : /************************************************************************/
    1127             : /*                           TestCapability()                           */
    1128             : /************************************************************************/
    1129             : 
    1130         342 : int OGRGMLLayer::TestCapability(const char *pszCap) const
    1131             : 
    1132             : {
    1133         342 :     if (EQUAL(pszCap, OLCSequentialWrite))
    1134          19 :         return bWriter;
    1135             : 
    1136         323 :     else if (EQUAL(pszCap, OLCCreateField))
    1137          18 :         return bWriter && m_iNextGMLId == 0;
    1138             : 
    1139         305 :     else if (EQUAL(pszCap, OLCCreateGeomField))
    1140           4 :         return bWriter && m_iNextGMLId == 0;
    1141             : 
    1142         301 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    1143             :     {
    1144           4 :         if (poFClass == nullptr)
    1145           0 :             return FALSE;
    1146             : 
    1147           4 :         double dfXMin = 0.0;
    1148           4 :         double dfXMax = 0.0;
    1149           4 :         double dfYMin = 0.0;
    1150           4 :         double dfYMax = 0.0;
    1151             : 
    1152           4 :         return poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax);
    1153             :     }
    1154             : 
    1155         297 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    1156             :     {
    1157           1 :         if (poFClass == nullptr || m_poFilterGeom != nullptr ||
    1158           1 :             m_poAttrQuery != nullptr)
    1159           0 :             return FALSE;
    1160             : 
    1161           1 :         return poFClass->GetFeatureCount() != -1;
    1162             :     }
    1163             : 
    1164         296 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1165          15 :         return TRUE;
    1166             : 
    1167         281 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    1168         146 :         return poDS->IsGML3Output();
    1169             : 
    1170         135 :     else if (EQUAL(pszCap, OLCZGeometries))
    1171           3 :         return TRUE;
    1172             : 
    1173             :     else
    1174         132 :         return FALSE;
    1175             : }
    1176             : 
    1177             : /************************************************************************/
    1178             : /*                            CreateField()                             */
    1179             : /************************************************************************/
    1180             : 
    1181         181 : OGRErr OGRGMLLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
    1182             : 
    1183             : {
    1184         181 :     if (!bWriter || m_iNextGMLId != 0)
    1185           0 :         return OGRERR_FAILURE;
    1186             : 
    1187             :     /* -------------------------------------------------------------------- */
    1188             :     /*      Enforce XML naming semantics on element name.                   */
    1189             :     /* -------------------------------------------------------------------- */
    1190         362 :     OGRFieldDefn oCleanCopy(poField);
    1191         181 :     char *pszName = CPLStrdup(poField->GetNameRef());
    1192         181 :     CPLCleanXMLElementName(pszName);
    1193             : 
    1194         181 :     if (strcmp(pszName, poField->GetNameRef()) != 0)
    1195             :     {
    1196           0 :         if (!bApproxOK)
    1197             :         {
    1198           0 :             CPLFree(pszName);
    1199           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1200             :                      "Unable to create field with name '%s', it would not\n"
    1201             :                      "be valid as an XML element name.",
    1202             :                      poField->GetNameRef());
    1203           0 :             return OGRERR_FAILURE;
    1204             :         }
    1205             : 
    1206           0 :         oCleanCopy.SetName(pszName);
    1207           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1208             :                  "Field name '%s' adjusted to '%s' to be a valid\n"
    1209             :                  "XML element name.",
    1210             :                  poField->GetNameRef(), pszName);
    1211             :     }
    1212             : 
    1213         181 :     CPLFree(pszName);
    1214             : 
    1215         181 :     poFeatureDefn->AddFieldDefn(&oCleanCopy);
    1216             : 
    1217         181 :     return OGRERR_NONE;
    1218             : }
    1219             : 
    1220             : /************************************************************************/
    1221             : /*                          CreateGeomField()                           */
    1222             : /************************************************************************/
    1223             : 
    1224          19 : OGRErr OGRGMLLayer::CreateGeomField(const OGRGeomFieldDefn *poField,
    1225             :                                     int bApproxOK)
    1226             : 
    1227             : {
    1228          19 :     if (!bWriter || m_iNextGMLId != 0)
    1229           0 :         return OGRERR_FAILURE;
    1230             : 
    1231             :     /* -------------------------------------------------------------------- */
    1232             :     /*      Enforce XML naming semantics on element name.                   */
    1233             :     /* -------------------------------------------------------------------- */
    1234          38 :     OGRGeomFieldDefn oCleanCopy(poField);
    1235          19 :     const auto poSRSOri = poField->GetSpatialRef();
    1236          19 :     poDS->DeclareNewWriteSRS(poSRSOri);
    1237          19 :     if (poSRSOri)
    1238             :     {
    1239           6 :         auto poSRS = poSRSOri->Clone();
    1240           6 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1241           6 :         oCleanCopy.SetSpatialRef(poSRS);
    1242           6 :         poSRS->Release();
    1243             :     }
    1244          19 :     char *pszName = CPLStrdup(poField->GetNameRef());
    1245          19 :     CPLCleanXMLElementName(pszName);
    1246             : 
    1247          19 :     if (strcmp(pszName, poField->GetNameRef()) != 0)
    1248             :     {
    1249           0 :         if (!bApproxOK)
    1250             :         {
    1251           0 :             CPLFree(pszName);
    1252           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1253             :                      "Unable to create field with name '%s', it would not\n"
    1254             :                      "be valid as an XML element name.",
    1255             :                      poField->GetNameRef());
    1256           0 :             return OGRERR_FAILURE;
    1257             :         }
    1258             : 
    1259           0 :         oCleanCopy.SetName(pszName);
    1260           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1261             :                  "Field name '%s' adjusted to '%s' to be a valid\n"
    1262             :                  "XML element name.",
    1263             :                  poField->GetNameRef(), pszName);
    1264             :     }
    1265             : 
    1266          19 :     CPLFree(pszName);
    1267             : 
    1268          19 :     poFeatureDefn->AddGeomFieldDefn(&oCleanCopy);
    1269             : 
    1270          19 :     return OGRERR_NONE;
    1271             : }
    1272             : 
    1273             : /************************************************************************/
    1274             : /*                             GetDataset()                             */
    1275             : /************************************************************************/
    1276             : 
    1277          20 : GDALDataset *OGRGMLLayer::GetDataset()
    1278             : {
    1279          20 :     return poDS;
    1280             : }

Generated by: LCOV version 1.14