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-08-01 10:10:57 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         704 : OGRGMLLayer::OGRGMLLayer(const char *pszName, bool bWriterIn,
      27         704 :                          OGRGMLDataSource *poDSIn)
      28             :     : poFeatureDefn(new OGRFeatureDefn(
      29         704 :           pszName + (STARTS_WITH_CI(pszName, "ogr:") ? 4 : 0))),
      30             :       iNextGMLId(0), bInvalidFIDFound(false), pszFIDPrefix(nullptr),
      31             :       bWriter(bWriterIn), poDS(poDSIn),
      32         704 :       poFClass(!bWriter ? poDS->GetReader()->GetClass(pszName) : nullptr),
      33             :       // Reader's should get the corresponding GMLFeatureClass and cache it.
      34        1408 :       hCacheSRS(GML_BuildOGRGeometryFromList_CreateCache()),
      35             :       // Compatibility option. Not advertized, because hopefully won't be
      36             :       // needed. Just put here in case.
      37             :       bUseOldFIDFormat(
      38         704 :           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        2816 :           CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
      43             : {
      44         704 :     SetDescription(poFeatureDefn->GetName());
      45         704 :     poFeatureDefn->Reference();
      46         704 :     poFeatureDefn->SetGeomType(wkbNone);
      47         704 : }
      48             : 
      49             : /************************************************************************/
      50             : /*                           ~OGRGMLLayer()                           */
      51             : /************************************************************************/
      52             : 
      53        1408 : OGRGMLLayer::~OGRGMLLayer()
      54             : 
      55             : {
      56         704 :     CPLFree(pszFIDPrefix);
      57             : 
      58         704 :     if (poFeatureDefn)
      59         704 :         poFeatureDefn->Release();
      60             : 
      61         704 :     GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
      62        1408 : }
      63             : 
      64             : /************************************************************************/
      65             : /*                            ResetReading()                            */
      66             : /************************************************************************/
      67             : 
      68         771 : void OGRGMLLayer::ResetReading()
      69             : 
      70             : {
      71         771 :     if (bWriter)
      72          16 :         return;
      73             : 
      74        1510 :     if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
      75         755 :         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         755 :     iNextGMLId = 0;
      88         755 :     poDS->GetReader()->ResetReading();
      89         755 :     CPLDebug("GML", "ResetReading()");
      90         755 :     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         579 : static GIntBig Increment(GIntBig nVal)
     105             : {
     106         579 :     if (nVal <= GINTBIG_MAX - 1)
     107         579 :         return nVal + 1;
     108           0 :     return nVal;
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                           GetNextFeature()                           */
     113             : /************************************************************************/
     114             : 
     115         842 : OGRFeature *OGRGMLLayer::GetNextFeature()
     116             : 
     117             : {
     118         842 :     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         826 :     if (poDS->GetLastReadLayer() != this)
     126             :     {
     127         395 :         if (poDS->GetReadMode() != INTERLEAVED_LAYERS)
     128         386 :             ResetReading();
     129         395 :         poDS->SetLastReadLayer(this);
     130             :     }
     131             : 
     132         826 :     const bool bSkipCorruptedFeatures = CPLFetchBool(
     133         826 :         poDS->GetOpenOptions(), "SKIP_CORRUPTED_FEATURES",
     134         826 :         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        1700 :         GMLFeature *poGMLFeature = poDS->PeekStoredGMLFeature();
     143        1700 :         if (poGMLFeature != nullptr)
     144             :         {
     145           3 :             poDS->SetStoredGMLFeature(nullptr);
     146             :         }
     147             :         else
     148             :         {
     149        1697 :             poGMLFeature = poDS->GetReader()->NextFeature();
     150        1697 :             if (poGMLFeature == nullptr)
     151         826 :                 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        1564 :             m_nFeaturesRead++;
     157             :         }
     158             : 
     159             :         /* --------------------------------------------------------------------
     160             :          */
     161             :         /*      Is it of the proper feature class? */
     162             :         /* --------------------------------------------------------------------
     163             :          */
     164             : 
     165        1567 :         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         739 :         GIntBig nFID = -1;
     193         739 :         const char *pszGML_FID = poGMLFeature->GetFID();
     194         739 :         if (bInvalidFIDFound)
     195             :         {
     196          44 :             nFID = iNextGMLId;
     197          44 :             iNextGMLId = Increment(iNextGMLId);
     198             :         }
     199         695 :         else if (pszGML_FID == nullptr)
     200             :         {
     201         115 :             bInvalidFIDFound = true;
     202         115 :             nFID = iNextGMLId;
     203         115 :             iNextGMLId = Increment(iNextGMLId);
     204             :         }
     205         580 :         else if (iNextGMLId == 0)
     206             :         {
     207         303 :             int j = 0;
     208         303 :             int i = static_cast<int>(strlen(pszGML_FID)) - 1;
     209         734 :             while (i >= 0 && pszGML_FID[i] >= '0' && pszGML_FID[i] <= '9' &&
     210             :                    j < 20)
     211             :             {
     212         431 :                 i--;
     213         431 :                 j++;
     214             :             }
     215             :             // i points the last character of the fid.
     216         303 :             if (i >= 0 && j < 20 && pszFIDPrefix == nullptr)
     217             :             {
     218         239 :                 pszFIDPrefix = static_cast<char *>(CPLMalloc(i + 2));
     219         239 :                 pszFIDPrefix[i + 1] = '\0';
     220         239 :                 strncpy(pszFIDPrefix, pszGML_FID, i + 1);
     221             :             }
     222             :             // pszFIDPrefix now contains the prefix or NULL if no prefix is
     223             :             // found.
     224         303 :             if (j < 20 && sscanf(pszGML_FID + i + 1, CPL_FRMT_GIB, &nFID) == 1)
     225             :             {
     226         294 :                 if (iNextGMLId <= nFID)
     227         294 :                     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         277 :             const char *pszFIDPrefix_notnull = pszFIDPrefix;
     239         277 :             if (pszFIDPrefix_notnull == nullptr)
     240           6 :                 pszFIDPrefix_notnull = "";
     241         277 :             int nLenPrefix = static_cast<int>(strlen(pszFIDPrefix_notnull));
     242             : 
     243         827 :             if (strncmp(pszGML_FID, pszFIDPrefix_notnull, nLenPrefix) == 0 &&
     244         550 :                 strlen(pszGML_FID + nLenPrefix) < 20 &&
     245         273 :                 sscanf(pszGML_FID + nLenPrefix, CPL_FRMT_GIB, &nFID) == 1)
     246             :             {
     247             :                 // fid with the prefix. Using its numerical part.
     248         272 :                 if (iNextGMLId < nFID)
     249         112 :                     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         739 :         OGRGeometry **papoGeometries = nullptr;
     268         739 :         const CPLXMLNode *const *papsGeometry = poGMLFeature->GetGeometryList();
     269             : 
     270         739 :         const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
     271             :         const CPLXMLNode *psBoundedByGeometry =
     272         739 :             poGMLFeature->GetBoundedByGeometry();
     273         739 :         if (psBoundedByGeometry && !(papsGeometry && papsGeometry[0]))
     274             :         {
     275           6 :             apsGeometries[0] = psBoundedByGeometry;
     276           6 :             papsGeometry = apsGeometries;
     277             :         }
     278             : 
     279         739 :         OGRGeometry *poGeom = nullptr;
     280             : 
     281         739 :         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         645 :         else if (papsGeometry[0] &&
     340         547 :                  strcmp(papsGeometry[0]->pszValue, "null") == 0)
     341             :         {
     342             :             // do nothing
     343             :         }
     344         641 :         else if (papsGeometry[0] != nullptr)
     345             :         {
     346         543 :             const char *pszSRSName = poDS->GetGlobalSRSName();
     347         543 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     348         543 :             poGeom = GML_BuildOGRGeometryFromList(
     349         543 :                 papsGeometry, true, poDS->GetInvertAxisOrderIfLatLong(),
     350         543 :                 pszSRSName, poDS->GetConsiderEPSGAsURN(),
     351         543 :                 poDS->GetSwapCoordinates(), poDS->GetSecondaryGeometryOption(),
     352         543 :                 hCacheSRS, bFaceHoleNegative);
     353         543 :             CPLPopErrorHandler();
     354             : 
     355             :             // Do geometry type changes if needed to match layer geometry type.
     356         543 :             if (poGeom != nullptr)
     357             :             {
     358         539 :                 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         539 :             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         715 :         int iDstField = 0;
     396         715 :         OGRFeature *poOGRFeature = new OGRFeature(poFeatureDefn);
     397             : 
     398         715 :         poOGRFeature->SetFID(nFID);
     399         715 :         if (poDS->ExposeId())
     400             :         {
     401         607 :             if (pszGML_FID)
     402         564 :                 poOGRFeature->SetField(iDstField, pszGML_FID);
     403         607 :             iDstField++;
     404             :         }
     405             : 
     406         715 :         const int nPropertyCount = poFClass->GetPropertyCount();
     407        3462 :         for (int iField = 0; iField < nPropertyCount; iField++, iDstField++)
     408             :         {
     409             :             const GMLProperty *psGMLProperty =
     410        2747 :                 poGMLFeature->GetProperty(iField);
     411        2747 :             if (psGMLProperty == nullptr || psGMLProperty->nSubProperties == 0)
     412         614 :                 continue;
     413             : 
     414        2133 :             if (EQUAL(psGMLProperty->papszSubProperties[0], OGR_GML_NULL))
     415             :             {
     416           8 :                 poOGRFeature->SetFieldNull(iDstField);
     417           8 :                 continue;
     418             :             }
     419             : 
     420        2125 :             switch (poFClass->GetProperty(iField)->GetType())
     421             :             {
     422         354 :                 case GMLPT_Real:
     423             :                 {
     424         354 :                     poOGRFeature->SetField(
     425             :                         iDstField,
     426         354 :                         CPLAtof(psGMLProperty->papszSubProperties[0]));
     427             :                 }
     428         354 :                 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        1572 :                 default:
     527        1572 :                     poOGRFeature->SetField(
     528        1572 :                         iDstField, psGMLProperty->papszSubProperties[0]);
     529        1572 :                     break;
     530             :             }
     531             :         }
     532             : 
     533         715 :         delete poGMLFeature;
     534         715 :         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         715 :         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         621 :             poOGRFeature->SetGeometryDirectly(poGeom);
     550             :         }
     551             : 
     552             :         // Assign SRS.
     553        1484 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
     554             :         {
     555         769 :             poGeom = poOGRFeature->GetGeomFieldRef(i);
     556         769 :             if (poGeom != nullptr)
     557             :             {
     558             :                 const OGRSpatialReference *poSRS =
     559         682 :                     poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef();
     560         682 :                 if (poSRS != nullptr)
     561         145 :                     poGeom->assignSpatialReference(poSRS);
     562             :             }
     563             :         }
     564             : 
     565             :         /* --------------------------------------------------------------------
     566             :          */
     567             :         /*      Test against the attribute query. */
     568             :         /* --------------------------------------------------------------------
     569             :          */
     570         715 :         if (m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poOGRFeature))
     571             :         {
     572          28 :             delete poOGRFeature;
     573          28 :             continue;
     574             :         }
     575             : 
     576             :         // Got the desired feature.
     577         687 :         return poOGRFeature;
     578         874 :     }
     579             : 
     580             :     return nullptr;
     581             : }
     582             : 
     583             : /************************************************************************/
     584             : /*                          GetFeatureCount()                           */
     585             : /************************************************************************/
     586             : 
     587          62 : GIntBig OGRGMLLayer::GetFeatureCount(int bForce)
     588             : 
     589             : {
     590          62 :     if (poFClass == nullptr)
     591           0 :         return 0;
     592             : 
     593          62 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     594           6 :         return OGRLayer::GetFeatureCount(bForce);
     595             : 
     596             :     // If the schema is read from a .xsd file, we haven't read
     597             :     // the feature count, so compute it now.
     598          56 :     GIntBig nFeatureCount = poFClass->GetFeatureCount();
     599          56 :     if (nFeatureCount < 0)
     600             :     {
     601          26 :         nFeatureCount = OGRLayer::GetFeatureCount(bForce);
     602          26 :         poFClass->SetFeatureCount(nFeatureCount);
     603             :     }
     604             : 
     605          56 :     return nFeatureCount;
     606             : }
     607             : 
     608             : /************************************************************************/
     609             : /*                            IGetExtent()                              */
     610             : /************************************************************************/
     611             : 
     612          10 : OGRErr OGRGMLLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
     613             :                                bool bForce)
     614             : 
     615             : {
     616          10 :     if (GetGeomType() == wkbNone)
     617           0 :         return OGRERR_FAILURE;
     618             : 
     619          10 :     double dfXMin = 0.0;
     620          10 :     double dfXMax = 0.0;
     621          10 :     double dfYMin = 0.0;
     622          10 :     double dfYMax = 0.0;
     623          20 :     if (poFClass != nullptr &&
     624          10 :         poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
     625             :     {
     626           8 :         psExtent->MinX = dfXMin;
     627           8 :         psExtent->MaxX = dfXMax;
     628           8 :         psExtent->MinY = dfYMin;
     629           8 :         psExtent->MaxY = dfYMax;
     630             : 
     631           8 :         return OGRERR_NONE;
     632             :     }
     633             : 
     634           2 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     635             : }
     636             : 
     637             : /************************************************************************/
     638             : /*                             GetExtent()                              */
     639             : /************************************************************************/
     640             : 
     641         505 : static void GMLWriteField(OGRGMLDataSource *poDS, VSILFILE *fp,
     642             :                           bool bWriteSpaceIndentation, const char *pszPrefix,
     643             :                           bool bRemoveAppPrefix, OGRFieldDefn *poFieldDefn,
     644             :                           const char *pszVal)
     645             : 
     646             : {
     647         505 :     const char *pszFieldName = poFieldDefn->GetNameRef();
     648             : 
     649         505 :     while (*pszVal == ' ')
     650           0 :         pszVal++;
     651             : 
     652         505 :     if (bWriteSpaceIndentation)
     653         505 :         VSIFPrintfL(fp, "      ");
     654             : 
     655         505 :     if (bRemoveAppPrefix)
     656          60 :         poDS->PrintLine(fp, "<%s>%s</%s>", pszFieldName, pszVal, pszFieldName);
     657             :     else
     658         445 :         poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix, pszFieldName,
     659             :                         pszVal, pszPrefix, pszFieldName);
     660         505 : }
     661             : 
     662             : /************************************************************************/
     663             : /*                           ICreateFeature()                            */
     664             : /************************************************************************/
     665             : 
     666         266 : OGRErr OGRGMLLayer::ICreateFeature(OGRFeature *poFeature)
     667             : 
     668             : {
     669         266 :     const bool bIsGML3Output = poDS->IsGML3Output();
     670         266 :     VSILFILE *fp = poDS->GetOutputFP();
     671         266 :     const bool bWriteSpaceIndentation = poDS->WriteSpaceIndentation();
     672         266 :     const char *pszPrefix = poDS->GetAppPrefix();
     673         266 :     const bool bRemoveAppPrefix = poDS->RemoveAppPrefix();
     674         266 :     const bool bGMLFeatureCollection = poDS->GMLFeatureCollection();
     675             : 
     676         266 :     if (!bWriter || poDS->HasWriteError())
     677           0 :         return OGRERR_FAILURE;
     678             : 
     679         266 :     poFeature->FillUnsetWithDefault(TRUE, nullptr);
     680         266 :     if (!poFeature->Validate(OGR_F_VAL_ALL & ~OGR_F_VAL_GEOM_TYPE &
     681             :                                  ~OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT,
     682             :                              TRUE))
     683           2 :         return OGRERR_FAILURE;
     684             : 
     685         264 :     if (bWriteSpaceIndentation)
     686         264 :         VSIFPrintfL(fp, "  ");
     687         264 :     if (bIsGML3Output && !bGMLFeatureCollection)
     688             :     {
     689         220 :         if (bRemoveAppPrefix)
     690          10 :             poDS->PrintLine(fp, "<featureMember>");
     691             :         else
     692         210 :             poDS->PrintLine(fp, "<%s:featureMember>", pszPrefix);
     693             :     }
     694             :     else
     695             :     {
     696          44 :         poDS->PrintLine(fp, "<gml:featureMember>");
     697             :     }
     698             : 
     699         264 :     if (poFeature->GetFID() == OGRNullFID)
     700         251 :         poFeature->SetFID(iNextGMLId++);
     701             : 
     702         264 :     if (bWriteSpaceIndentation)
     703         264 :         VSIFPrintfL(fp, "    ");
     704         264 :     VSIFPrintfL(fp, "<");
     705         264 :     if (!bRemoveAppPrefix)
     706         244 :         VSIFPrintfL(fp, "%s:", pszPrefix);
     707             : 
     708         264 :     int nGMLIdIndex = -1;
     709         264 :     if (bIsGML3Output)
     710             :     {
     711         230 :         nGMLIdIndex = poFeatureDefn->GetFieldIndex("gml_id");
     712         230 :         if (nGMLIdIndex >= 0 && poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
     713           3 :             poDS->PrintLine(fp, "%s gml:id=\"%s\">", poFeatureDefn->GetName(),
     714             :                             poFeature->GetFieldAsString(nGMLIdIndex));
     715             :         else
     716         454 :             poDS->PrintLine(fp, "%s gml:id=\"%s." CPL_FRMT_GIB "\">",
     717         227 :                             poFeatureDefn->GetName(), poFeatureDefn->GetName(),
     718             :                             poFeature->GetFID());
     719             :     }
     720             :     else
     721             :     {
     722          34 :         nGMLIdIndex = poFeatureDefn->GetFieldIndex("fid");
     723          34 :         if (bUseOldFIDFormat)
     724             :         {
     725           0 :             poDS->PrintLine(fp, "%s fid=\"F" CPL_FRMT_GIB "\">",
     726           0 :                             poFeatureDefn->GetName(), poFeature->GetFID());
     727             :         }
     728          44 :         else if (nGMLIdIndex >= 0 &&
     729          10 :                  poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
     730             :         {
     731          10 :             poDS->PrintLine(fp, "%s fid=\"%s\">", poFeatureDefn->GetName(),
     732             :                             poFeature->GetFieldAsString(nGMLIdIndex));
     733             :         }
     734             :         else
     735             :         {
     736          48 :             poDS->PrintLine(fp, "%s fid=\"%s." CPL_FRMT_GIB "\">",
     737          24 :                             poFeatureDefn->GetName(), poFeatureDefn->GetName(),
     738             :                             poFeature->GetFID());
     739             :         }
     740             :     }
     741             : 
     742         517 :     for (int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount();
     743             :          iGeomField++)
     744             :     {
     745             :         const OGRGeomFieldDefn *poFieldDefn =
     746         253 :             poFeatureDefn->GetGeomFieldDefn(iGeomField);
     747             : 
     748             :         // Write out Geometry - for now it isn't indented properly.
     749             :         // GML geometries don't like very much the concept of empty geometry.
     750         253 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField);
     751         253 :         if (poGeom != nullptr && !poGeom->IsEmpty())
     752             :         {
     753         215 :             OGREnvelope3D sGeomBounds;
     754             : 
     755         215 :             const int nCoordDimension = poGeom->getCoordinateDimension();
     756             : 
     757         215 :             poGeom->getEnvelope(&sGeomBounds);
     758         215 :             if (poDS->HasWriteGlobalSRS())
     759         211 :                 poDS->GrowExtents(&sGeomBounds, nCoordDimension);
     760             : 
     761         356 :             if (poGeom->getSpatialReference() == nullptr &&
     762         141 :                 poFieldDefn->GetSpatialRef() != nullptr)
     763          18 :                 poGeom->assignSpatialReference(poFieldDefn->GetSpatialRef());
     764             : 
     765         215 :             const auto &oCoordPrec = poFieldDefn->GetCoordinatePrecision();
     766             : 
     767         215 :             if (bIsGML3Output && poDS->WriteFeatureBoundedBy())
     768             :             {
     769         171 :                 bool bCoordSwap = false;
     770             : 
     771             :                 char *pszSRSName =
     772         171 :                     GML_GetSRSName(poGeom->getSpatialReference(),
     773         171 :                                    poDS->GetSRSNameFormat(), &bCoordSwap);
     774         171 :                 char szLowerCorner[75] = {};
     775         171 :                 char szUpperCorner[75] = {};
     776             : 
     777         171 :                 OGRWktOptions coordOpts;
     778             : 
     779         171 :                 if (oCoordPrec.dfXYResolution !=
     780             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     781             :                 {
     782           3 :                     coordOpts.format = OGRWktFormat::F;
     783           3 :                     coordOpts.xyPrecision =
     784           3 :                         OGRGeomCoordinatePrecision::ResolutionToPrecision(
     785           3 :                             oCoordPrec.dfXYResolution);
     786             :                 }
     787         171 :                 if (oCoordPrec.dfZResolution !=
     788             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     789             :                 {
     790           3 :                     coordOpts.format = OGRWktFormat::F;
     791           3 :                     coordOpts.zPrecision =
     792           3 :                         OGRGeomCoordinatePrecision::ResolutionToPrecision(
     793           3 :                             oCoordPrec.dfZResolution);
     794             :                 }
     795             : 
     796         342 :                 std::string wkt;
     797         171 :                 if (bCoordSwap)
     798             :                 {
     799          36 :                     wkt = OGRMakeWktCoordinate(
     800             :                         sGeomBounds.MinY, sGeomBounds.MinX, sGeomBounds.MinZ,
     801          18 :                         nCoordDimension, coordOpts);
     802          18 :                     memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
     803             : 
     804          36 :                     wkt = OGRMakeWktCoordinate(
     805             :                         sGeomBounds.MaxY, sGeomBounds.MaxX, sGeomBounds.MaxZ,
     806          18 :                         nCoordDimension, coordOpts);
     807          18 :                     memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
     808             :                 }
     809             :                 else
     810             :                 {
     811         306 :                     wkt = OGRMakeWktCoordinate(
     812             :                         sGeomBounds.MinX, sGeomBounds.MinY, sGeomBounds.MinZ,
     813         153 :                         nCoordDimension, coordOpts);
     814         153 :                     memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
     815             : 
     816         306 :                     wkt = OGRMakeWktCoordinate(
     817             :                         sGeomBounds.MaxX, sGeomBounds.MaxY, sGeomBounds.MaxZ,
     818         153 :                         nCoordDimension, coordOpts);
     819         153 :                     memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
     820             :                 }
     821         171 :                 if (bWriteSpaceIndentation)
     822         171 :                     VSIFPrintfL(fp, "      ");
     823         171 :                 poDS->PrintLine(
     824             :                     fp,
     825             :                     "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s"
     826             :                     "</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner>"
     827             :                     "</gml:Envelope></gml:boundedBy>",
     828             :                     (nCoordDimension == 3) ? " srsDimension=\"3\"" : "",
     829             :                     pszSRSName, szLowerCorner, szUpperCorner);
     830         171 :                 CPLFree(pszSRSName);
     831             :             }
     832             : 
     833         215 :             char **papszOptions = nullptr;
     834         215 :             if (bIsGML3Output)
     835             :             {
     836         181 :                 papszOptions = CSLAddString(papszOptions, "FORMAT=GML3");
     837         181 :                 if (poDS->GetSRSNameFormat() == SRSNAME_SHORT)
     838             :                     papszOptions =
     839           1 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=SHORT");
     840         180 :                 else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URN)
     841             :                     papszOptions =
     842         167 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URN");
     843          13 :                 else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URL)
     844             :                     papszOptions =
     845          13 :                         CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URL");
     846             :             }
     847         215 :             const char *pszSRSDimensionLoc = poDS->GetSRSDimensionLoc();
     848         215 :             if (pszSRSDimensionLoc != nullptr)
     849           3 :                 papszOptions = CSLSetNameValue(papszOptions, "SRSDIMENSION_LOC",
     850             :                                                pszSRSDimensionLoc);
     851         215 :             if (poDS->IsGML32Output())
     852             :             {
     853         115 :                 if (poFeatureDefn->GetGeomFieldCount() > 1)
     854          18 :                     papszOptions = CSLAddString(
     855             :                         papszOptions, CPLSPrintf("GMLID=%s.%s." CPL_FRMT_GIB,
     856           9 :                                                  poFeatureDefn->GetName(),
     857             :                                                  poFieldDefn->GetNameRef(),
     858             :                                                  poFeature->GetFID()));
     859             :                 else
     860         212 :                     papszOptions = CSLAddString(
     861             :                         papszOptions, CPLSPrintf("GMLID=%s.geom." CPL_FRMT_GIB,
     862         106 :                                                  poFeatureDefn->GetName(),
     863             :                                                  poFeature->GetFID()));
     864             :             }
     865             : 
     866         215 :             if (oCoordPrec.dfXYResolution !=
     867             :                 OGRGeomCoordinatePrecision::UNKNOWN)
     868             :             {
     869           3 :                 papszOptions = CSLAddString(
     870             :                     papszOptions, CPLSPrintf("XY_COORD_RESOLUTION=%g",
     871           3 :                                              oCoordPrec.dfXYResolution));
     872             :             }
     873         215 :             if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     874             :             {
     875           3 :                 papszOptions = CSLAddString(
     876             :                     papszOptions, CPLSPrintf("Z_COORD_RESOLUTION=%g",
     877           3 :                                              oCoordPrec.dfZResolution));
     878             :             }
     879             : 
     880         215 :             char *pszGeometry = nullptr;
     881         215 :             if (!bIsGML3Output && OGR_GT_IsNonLinear(poGeom->getGeometryType()))
     882             :             {
     883           0 :                 OGRGeometry *poGeomTmp = OGRGeometryFactory::forceTo(
     884           0 :                     poGeom->clone(),
     885           0 :                     OGR_GT_GetLinear(poGeom->getGeometryType()));
     886           0 :                 pszGeometry = poGeomTmp->exportToGML(papszOptions);
     887           0 :                 delete poGeomTmp;
     888             :             }
     889             :             else
     890             :             {
     891         215 :                 if (wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
     892             :                 {
     893           0 :                     pszGeometry = poGeom->exportToGML(papszOptions);
     894             : 
     895             :                     const char *pszGMLID =
     896           0 :                         poDS->IsGML32Output()
     897           0 :                             ? CPLSPrintf(
     898             :                                   " gml:id=\"%s\"",
     899             :                                   CSLFetchNameValue(papszOptions, "GMLID"))
     900           0 :                             : "";
     901           0 :                     char *pszNewGeom = CPLStrdup(
     902             :                         CPLSPrintf("<gml:TriangulatedSurface%s><gml:patches>%s<"
     903             :                                    "/gml:patches></gml:TriangulatedSurface>",
     904             :                                    pszGMLID, pszGeometry));
     905           0 :                     CPLFree(pszGeometry);
     906           0 :                     pszGeometry = pszNewGeom;
     907             :                 }
     908             :                 else
     909             :                 {
     910         215 :                     pszGeometry = poGeom->exportToGML(papszOptions);
     911             :                 }
     912             :             }
     913         215 :             CSLDestroy(papszOptions);
     914         215 :             if (pszGeometry)
     915             :             {
     916         214 :                 if (bWriteSpaceIndentation)
     917         214 :                     VSIFPrintfL(fp, "      ");
     918         214 :                 if (bRemoveAppPrefix)
     919          20 :                     poDS->PrintLine(fp, "<%s>%s</%s>",
     920             :                                     poFieldDefn->GetNameRef(), pszGeometry,
     921             :                                     poFieldDefn->GetNameRef());
     922             :                 else
     923         194 :                     poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix,
     924             :                                     poFieldDefn->GetNameRef(), pszGeometry,
     925             :                                     pszPrefix, poFieldDefn->GetNameRef());
     926             :             }
     927             :             else
     928             :             {
     929           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     930             :                          "Export of geometry to GML failed");
     931             :             }
     932         215 :             CPLFree(pszGeometry);
     933             :         }
     934             :     }
     935             : 
     936             :     // Write all "set" fields.
     937         885 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     938             :     {
     939         621 :         if (iField == nGMLIdIndex)
     940          13 :             continue;
     941         608 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
     942             : 
     943         608 :         if (poFeature->IsFieldNull(iField))
     944             :         {
     945           1 :             const char *pszFieldName = poFieldDefn->GetNameRef();
     946             : 
     947           1 :             if (bWriteSpaceIndentation)
     948           1 :                 VSIFPrintfL(fp, "      ");
     949             : 
     950           1 :             if (bRemoveAppPrefix)
     951           0 :                 poDS->PrintLine(fp, "<%s xsi:nil=\"true\"/>", pszFieldName);
     952             :             else
     953           1 :                 poDS->PrintLine(fp, "<%s:%s xsi:nil=\"true\"/>", pszPrefix,
     954             :                                 pszFieldName);
     955             :         }
     956         607 :         else if (poFeature->IsFieldSet(iField))
     957             :         {
     958         500 :             OGRFieldType eType = poFieldDefn->GetType();
     959         500 :             if (eType == OFTStringList)
     960             :             {
     961           1 :                 char **papszIter = poFeature->GetFieldAsStringList(iField);
     962           3 :                 while (papszIter != nullptr && *papszIter != nullptr)
     963             :                 {
     964           2 :                     char *pszEscaped = OGRGetXML_UTF8_EscapedString(*papszIter);
     965           2 :                     GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
     966             :                                   bRemoveAppPrefix, poFieldDefn, pszEscaped);
     967           2 :                     CPLFree(pszEscaped);
     968             : 
     969           2 :                     papszIter++;
     970             :                 }
     971             :             }
     972         499 :             else if (eType == OFTIntegerList)
     973             :             {
     974           2 :                 int nCount = 0;
     975             :                 const int *panVals =
     976           2 :                     poFeature->GetFieldAsIntegerList(iField, &nCount);
     977           2 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
     978             :                 {
     979           3 :                     for (int i = 0; i < nCount; i++)
     980             :                     {
     981             :                         // 0 and 1 are OK, but the canonical representation is
     982             :                         // false and true.
     983           2 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
     984             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
     985           2 :                                       panVals[i] ? "true" : "false");
     986             :                     }
     987             :                 }
     988             :                 else
     989             :                 {
     990           3 :                     for (int i = 0; i < nCount; i++)
     991             :                     {
     992           2 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
     993             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
     994           2 :                                       CPLSPrintf("%d", panVals[i]));
     995             :                     }
     996             :                 }
     997             :             }
     998         497 :             else if (eType == OFTInteger64List)
     999             :             {
    1000           2 :                 int nCount = 0;
    1001             :                 const GIntBig *panVals =
    1002           2 :                     poFeature->GetFieldAsInteger64List(iField, &nCount);
    1003           2 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1004             :                 {
    1005           0 :                     for (int i = 0; i < nCount; i++)
    1006             :                     {
    1007             :                         // 0 and 1 are OK, but the canonical representation is
    1008             :                         // false and true.
    1009           0 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1010             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1011           0 :                                       panVals[i] ? "true" : "false");
    1012             :                     }
    1013             :                 }
    1014             :                 else
    1015             :                 {
    1016           5 :                     for (int i = 0; i < nCount; i++)
    1017             :                     {
    1018           3 :                         GMLWriteField(poDS, fp, bWriteSpaceIndentation,
    1019             :                                       pszPrefix, bRemoveAppPrefix, poFieldDefn,
    1020           3 :                                       CPLSPrintf(CPL_FRMT_GIB, panVals[i]));
    1021             :                     }
    1022             :                 }
    1023             :             }
    1024         495 :             else if (eType == OFTRealList)
    1025             :             {
    1026           1 :                 int nCount = 0;
    1027             :                 const double *padfVals =
    1028           1 :                     poFeature->GetFieldAsDoubleList(iField, &nCount);
    1029           3 :                 for (int i = 0; i < nCount; i++)
    1030             :                 {
    1031           2 :                     char szBuffer[80] = {};
    1032           2 :                     CPLsnprintf(szBuffer, sizeof(szBuffer), "%.15g",
    1033           2 :                                 padfVals[i]);
    1034           2 :                     GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1035             :                                   bRemoveAppPrefix, poFieldDefn, szBuffer);
    1036             :                 }
    1037             :             }
    1038         625 :             else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1039         131 :                      poFieldDefn->GetSubType() == OFSTBoolean)
    1040             :             {
    1041             :                 // 0 and 1 are OK, but the canonical representation is false and
    1042             :                 // true.
    1043           2 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1044             :                               bRemoveAppPrefix, poFieldDefn,
    1045           2 :                               (poFeature->GetFieldAsInteger(iField)) ? "true"
    1046             :                                                                      : "false");
    1047             :             }
    1048         492 :             else if (eType == OFTDate)
    1049             :             {
    1050          49 :                 const OGRField *poField = poFeature->GetRawFieldRef(iField);
    1051             :                 const char *pszXML =
    1052          98 :                     CPLSPrintf("%04d-%02d-%02d", poField->Date.Year,
    1053          49 :                                poField->Date.Month, poField->Date.Day);
    1054          49 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1055             :                               bRemoveAppPrefix, poFieldDefn, pszXML);
    1056             :             }
    1057         443 :             else if (eType == OFTDateTime)
    1058             :             {
    1059             :                 char *pszXML =
    1060          49 :                     OGRGetXMLDateTime(poFeature->GetRawFieldRef(iField));
    1061          49 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1062             :                               bRemoveAppPrefix, poFieldDefn, pszXML);
    1063          49 :                 CPLFree(pszXML);
    1064             :             }
    1065             :             else
    1066             :             {
    1067         394 :                 const char *pszRaw = poFeature->GetFieldAsString(iField);
    1068             : 
    1069         394 :                 char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
    1070             : 
    1071         394 :                 GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
    1072             :                               bRemoveAppPrefix, poFieldDefn, pszEscaped);
    1073         394 :                 CPLFree(pszEscaped);
    1074             :             }
    1075             :         }
    1076             :     }
    1077             : 
    1078         264 :     if (bWriteSpaceIndentation)
    1079         264 :         VSIFPrintfL(fp, "    ");
    1080         264 :     if (bRemoveAppPrefix)
    1081          20 :         poDS->PrintLine(fp, "</%s>", poFeatureDefn->GetName());
    1082             :     else
    1083         244 :         poDS->PrintLine(fp, "</%s:%s>", pszPrefix, poFeatureDefn->GetName());
    1084         264 :     if (bWriteSpaceIndentation)
    1085         264 :         VSIFPrintfL(fp, "  ");
    1086         264 :     if (bIsGML3Output && !bGMLFeatureCollection)
    1087             :     {
    1088         220 :         if (bRemoveAppPrefix)
    1089          10 :             poDS->PrintLine(fp, "</featureMember>");
    1090             :         else
    1091         210 :             poDS->PrintLine(fp, "</%s:featureMember>", pszPrefix);
    1092             :     }
    1093             :     else
    1094             :     {
    1095          44 :         poDS->PrintLine(fp, "</gml:featureMember>");
    1096             :     }
    1097             : 
    1098         264 :     return !poDS->HasWriteError() ? OGRERR_NONE : OGRERR_FAILURE;
    1099             : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                           TestCapability()                           */
    1103             : /************************************************************************/
    1104             : 
    1105         341 : int OGRGMLLayer::TestCapability(const char *pszCap)
    1106             : 
    1107             : {
    1108         341 :     if (EQUAL(pszCap, OLCSequentialWrite))
    1109          19 :         return bWriter;
    1110             : 
    1111         322 :     else if (EQUAL(pszCap, OLCCreateField))
    1112          18 :         return bWriter && iNextGMLId == 0;
    1113             : 
    1114         304 :     else if (EQUAL(pszCap, OLCCreateGeomField))
    1115           4 :         return bWriter && iNextGMLId == 0;
    1116             : 
    1117         300 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    1118             :     {
    1119           4 :         if (poFClass == nullptr)
    1120           0 :             return FALSE;
    1121             : 
    1122           4 :         double dfXMin = 0.0;
    1123           4 :         double dfXMax = 0.0;
    1124           4 :         double dfYMin = 0.0;
    1125           4 :         double dfYMax = 0.0;
    1126             : 
    1127           4 :         return poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax);
    1128             :     }
    1129             : 
    1130         296 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    1131             :     {
    1132           1 :         if (poFClass == nullptr || m_poFilterGeom != nullptr ||
    1133           1 :             m_poAttrQuery != nullptr)
    1134           0 :             return FALSE;
    1135             : 
    1136           1 :         return poFClass->GetFeatureCount() != -1;
    1137             :     }
    1138             : 
    1139         295 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1140          15 :         return TRUE;
    1141             : 
    1142         280 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    1143         146 :         return poDS->IsGML3Output();
    1144             : 
    1145         134 :     else if (EQUAL(pszCap, OLCZGeometries))
    1146           3 :         return TRUE;
    1147             : 
    1148             :     else
    1149         131 :         return FALSE;
    1150             : }
    1151             : 
    1152             : /************************************************************************/
    1153             : /*                            CreateField()                             */
    1154             : /************************************************************************/
    1155             : 
    1156         181 : OGRErr OGRGMLLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
    1157             : 
    1158             : {
    1159         181 :     if (!bWriter || iNextGMLId != 0)
    1160           0 :         return OGRERR_FAILURE;
    1161             : 
    1162             :     /* -------------------------------------------------------------------- */
    1163             :     /*      Enforce XML naming semantics on element name.                   */
    1164             :     /* -------------------------------------------------------------------- */
    1165         362 :     OGRFieldDefn oCleanCopy(poField);
    1166         181 :     char *pszName = CPLStrdup(poField->GetNameRef());
    1167         181 :     CPLCleanXMLElementName(pszName);
    1168             : 
    1169         181 :     if (strcmp(pszName, poField->GetNameRef()) != 0)
    1170             :     {
    1171           0 :         if (!bApproxOK)
    1172             :         {
    1173           0 :             CPLFree(pszName);
    1174           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1175             :                      "Unable to create field with name '%s', it would not\n"
    1176             :                      "be valid as an XML element name.",
    1177             :                      poField->GetNameRef());
    1178           0 :             return OGRERR_FAILURE;
    1179             :         }
    1180             : 
    1181           0 :         oCleanCopy.SetName(pszName);
    1182           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1183             :                  "Field name '%s' adjusted to '%s' to be a valid\n"
    1184             :                  "XML element name.",
    1185             :                  poField->GetNameRef(), pszName);
    1186             :     }
    1187             : 
    1188         181 :     CPLFree(pszName);
    1189             : 
    1190         181 :     poFeatureDefn->AddFieldDefn(&oCleanCopy);
    1191             : 
    1192         181 :     return OGRERR_NONE;
    1193             : }
    1194             : 
    1195             : /************************************************************************/
    1196             : /*                          CreateGeomField()                           */
    1197             : /************************************************************************/
    1198             : 
    1199          19 : OGRErr OGRGMLLayer::CreateGeomField(const OGRGeomFieldDefn *poField,
    1200             :                                     int bApproxOK)
    1201             : 
    1202             : {
    1203          19 :     if (!bWriter || iNextGMLId != 0)
    1204           0 :         return OGRERR_FAILURE;
    1205             : 
    1206             :     /* -------------------------------------------------------------------- */
    1207             :     /*      Enforce XML naming semantics on element name.                   */
    1208             :     /* -------------------------------------------------------------------- */
    1209          38 :     OGRGeomFieldDefn oCleanCopy(poField);
    1210          19 :     const auto poSRSOri = poField->GetSpatialRef();
    1211          19 :     poDS->DeclareNewWriteSRS(poSRSOri);
    1212          19 :     if (poSRSOri)
    1213             :     {
    1214           6 :         auto poSRS = poSRSOri->Clone();
    1215           6 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1216           6 :         oCleanCopy.SetSpatialRef(poSRS);
    1217           6 :         poSRS->Release();
    1218             :     }
    1219          19 :     char *pszName = CPLStrdup(poField->GetNameRef());
    1220          19 :     CPLCleanXMLElementName(pszName);
    1221             : 
    1222          19 :     if (strcmp(pszName, poField->GetNameRef()) != 0)
    1223             :     {
    1224           0 :         if (!bApproxOK)
    1225             :         {
    1226           0 :             CPLFree(pszName);
    1227           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1228             :                      "Unable to create field with name '%s', it would not\n"
    1229             :                      "be valid as an XML element name.",
    1230             :                      poField->GetNameRef());
    1231           0 :             return OGRERR_FAILURE;
    1232             :         }
    1233             : 
    1234           0 :         oCleanCopy.SetName(pszName);
    1235           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1236             :                  "Field name '%s' adjusted to '%s' to be a valid\n"
    1237             :                  "XML element name.",
    1238             :                  poField->GetNameRef(), pszName);
    1239             :     }
    1240             : 
    1241          19 :     CPLFree(pszName);
    1242             : 
    1243          19 :     poFeatureDefn->AddGeomFieldDefn(&oCleanCopy);
    1244             : 
    1245          19 :     return OGRERR_NONE;
    1246             : }
    1247             : 
    1248             : /************************************************************************/
    1249             : /*                             GetDataset()                             */
    1250             : /************************************************************************/
    1251             : 
    1252          18 : GDALDataset *OGRGMLLayer::GetDataset()
    1253             : {
    1254          18 :     return poDS;
    1255             : }

Generated by: LCOV version 1.14