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

Generated by: LCOV version 1.14