LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 660 688 95.9 %
Date: 2025-05-31 00:00:17 Functions: 27 27 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  OGR
       3             :  * Purpose:  OGRGMLASDriver implementation
       4             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       5             :  *
       6             :  * Initial development funded by the European Earth observation programme
       7             :  * Copernicus
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_gmlas.h"
      16             : 
      17             : #include "memdataset.h"
      18             : #include "cpl_sha256.h"
      19             : 
      20             : #include <algorithm>
      21             : 
      22             : /************************************************************************/
      23             : /*                 XercesInitializer::XercesInitializer()               */
      24             : /************************************************************************/
      25             : 
      26         195 : OGRGMLASDataSource::XercesInitializer::XercesInitializer()
      27             : {
      28         195 :     OGRInitializeXerces();
      29         195 : }
      30             : 
      31             : /************************************************************************/
      32             : /*                 XercesInitializer::~XercesInitializer()              */
      33             : /************************************************************************/
      34             : 
      35         195 : OGRGMLASDataSource::XercesInitializer::~XercesInitializer()
      36             : {
      37         195 :     OGRDeinitializeXerces();
      38         195 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                          OGRGMLASDataSource()                        */
      42             : /************************************************************************/
      43             : 
      44         195 : OGRGMLASDataSource::OGRGMLASDataSource()
      45         195 :     : m_poFieldsMetadataLayer(std::make_unique<OGRMemLayer>(
      46           0 :           szOGR_FIELDS_METADATA, nullptr, wkbNone)),
      47         195 :       m_poLayersMetadataLayer(std::make_unique<OGRMemLayer>(
      48           0 :           szOGR_LAYERS_METADATA, nullptr, wkbNone)),
      49         195 :       m_poRelationshipsLayer(std::make_unique<OGRMemLayer>(
      50           0 :           szOGR_LAYER_RELATIONSHIPS, nullptr, wkbNone)),
      51             :       m_poOtherMetadataLayer(
      52         780 :           std::make_unique<OGRMemLayer>(szOGR_OTHER_METADATA, nullptr, wkbNone))
      53             : {
      54             :     // Initialize m_poFieldsMetadataLayer
      55             :     {
      56         390 :         OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
      57         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      58             :     }
      59             :     {
      60         390 :         OGRFieldDefn oFieldDefn(szFIELD_INDEX, OFTInteger);
      61         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      62             :     }
      63             :     {
      64         390 :         OGRFieldDefn oFieldDefn(szFIELD_NAME, OFTString);
      65         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      66             :     }
      67             :     {
      68         390 :         OGRFieldDefn oFieldDefn(szFIELD_XPATH, OFTString);
      69         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      70             :     }
      71             :     {
      72         390 :         OGRFieldDefn oFieldDefn(szFIELD_TYPE, OFTString);
      73         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      74             :     }
      75             :     {
      76         390 :         OGRFieldDefn oFieldDefn(szFIELD_IS_LIST, OFTInteger);
      77         195 :         oFieldDefn.SetSubType(OFSTBoolean);
      78         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      79             :     }
      80             :     {
      81         390 :         OGRFieldDefn oFieldDefn(szFIELD_MIN_OCCURS, OFTInteger);
      82         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      83             :     }
      84             :     {
      85         390 :         OGRFieldDefn oFieldDefn(szFIELD_MAX_OCCURS, OFTInteger);
      86         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      87             :     }
      88             :     {
      89         390 :         OGRFieldDefn oFieldDefn(szFIELD_REPETITION_ON_SEQUENCE, OFTInteger);
      90         195 :         oFieldDefn.SetSubType(OFSTBoolean);
      91         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      92             :     }
      93             :     {
      94         390 :         OGRFieldDefn oFieldDefn(szFIELD_DEFAULT_VALUE, OFTString);
      95         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      96             :     }
      97             :     {
      98         390 :         OGRFieldDefn oFieldDefn(szFIELD_FIXED_VALUE, OFTString);
      99         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     100             :     }
     101             :     {
     102         390 :         OGRFieldDefn oFieldDefn(szFIELD_CATEGORY, OFTString);
     103         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     104             :     }
     105             :     {
     106         390 :         OGRFieldDefn oFieldDefn(szFIELD_RELATED_LAYER, OFTString);
     107         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     108             :     }
     109             :     {
     110         390 :         OGRFieldDefn oFieldDefn(szFIELD_JUNCTION_LAYER, OFTString);
     111         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     112             :     }
     113             :     {
     114         390 :         OGRFieldDefn oFieldDefn(szFIELD_DOCUMENTATION, OFTString);
     115         195 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     116             :     }
     117             : 
     118             :     // Initialize m_poLayersMetadataLayer
     119             :     {
     120         390 :         OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
     121         195 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     122             :     }
     123             :     {
     124         390 :         OGRFieldDefn oFieldDefn(szLAYER_XPATH, OFTString);
     125         195 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     126             :     }
     127             :     {
     128         390 :         OGRFieldDefn oFieldDefn(szLAYER_CATEGORY, OFTString);
     129         195 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     130             :     }
     131             :     {
     132         390 :         OGRFieldDefn oFieldDefn(szLAYER_PKID_NAME, OFTString);
     133         195 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     134             :     }
     135             :     {
     136         390 :         OGRFieldDefn oFieldDefn(szLAYER_PARENT_PKID_NAME, OFTString);
     137         195 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     138             :     }
     139             :     {
     140         390 :         OGRFieldDefn oFieldDefn(szLAYER_DOCUMENTATION, OFTString);
     141         195 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     142             :     }
     143             : 
     144             :     // Initialize m_poRelationshipsLayer
     145             :     {
     146         390 :         OGRFieldDefn oFieldDefn(szPARENT_LAYER, OFTString);
     147         195 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     148             :     }
     149             :     {
     150         390 :         OGRFieldDefn oFieldDefn(szPARENT_PKID, OFTString);
     151         195 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     152             :     }
     153             :     {
     154         390 :         OGRFieldDefn oFieldDefn(szPARENT_ELEMENT_NAME, OFTString);
     155         195 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     156             :     }
     157             :     {
     158         390 :         OGRFieldDefn oFieldDefn(szCHILD_LAYER, OFTString);
     159         195 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     160             :     }
     161             :     {
     162         390 :         OGRFieldDefn oFieldDefn(szCHILD_PKID, OFTString);
     163         195 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     164             :     }
     165             : 
     166             :     // Initialize m_poOtherMetadataLayer
     167             :     {
     168         390 :         OGRFieldDefn oFieldDefn(szKEY, OFTString);
     169         195 :         m_poOtherMetadataLayer->CreateField(&oFieldDefn);
     170             :     }
     171             :     {
     172         390 :         OGRFieldDefn oFieldDefn(szVALUE, OFTString);
     173         195 :         m_poOtherMetadataLayer->CreateField(&oFieldDefn);
     174             :     }
     175         195 : }
     176             : 
     177             : /************************************************************************/
     178             : /*                        ~OGRGMLASDataSource()                         */
     179             : /************************************************************************/
     180             : 
     181         390 : OGRGMLASDataSource::~OGRGMLASDataSource()
     182             : {
     183         195 :     if (m_bUnlinkConfigFileAfterUse)
     184             :     {
     185           0 :         VSIUnlink(m_osConfigFile.c_str());
     186             :     }
     187         390 : }
     188             : 
     189             : /************************************************************************/
     190             : /*                            GetLayerCount()                           */
     191             : /************************************************************************/
     192             : 
     193        8514 : int OGRGMLASDataSource::GetLayerCount()
     194             : {
     195        8514 :     return static_cast<int>(m_apoLayers.size() +
     196        8514 :                             m_apoRequestedMetadataLayers.size());
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                                GetLayer()                            */
     201             : /************************************************************************/
     202             : 
     203        8906 : OGRLayer *OGRGMLASDataSource::GetLayer(int i)
     204             : {
     205        8906 :     const int nBaseLayers = static_cast<int>(m_apoLayers.size());
     206        8906 :     if (i >= nBaseLayers)
     207             :     {
     208         153 :         RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
     209         153 :         if (i - nBaseLayers <
     210         153 :             static_cast<int>(m_apoRequestedMetadataLayers.size()))
     211         152 :             return m_apoRequestedMetadataLayers[i - nBaseLayers];
     212             :     }
     213             : 
     214        8754 :     if (i < 0 || i >= nBaseLayers)
     215           2 :         return nullptr;
     216        8752 :     return m_apoLayers[i].get();
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                             GetLayerByName()                         */
     221             : /************************************************************************/
     222             : 
     223         600 : OGRLayer *OGRGMLASDataSource::GetLayerByName(const char *pszName)
     224             : {
     225         600 :     if (OGRLayer *poLayer = GDALDataset::GetLayerByName(pszName))
     226         576 :         return poLayer;
     227             : 
     228             :     OGRLayer *apoLayers[] = {
     229          24 :         m_poFieldsMetadataLayer.get(), m_poLayersMetadataLayer.get(),
     230          24 :         m_poRelationshipsLayer.get(), m_poOtherMetadataLayer.get()};
     231          52 :     for (auto *poLayer : apoLayers)
     232             :     {
     233          51 :         if (EQUAL(pszName, poLayer->GetName()))
     234             :         {
     235          23 :             if (std::find(m_apoRequestedMetadataLayers.begin(),
     236             :                           m_apoRequestedMetadataLayers.end(),
     237          23 :                           poLayer) == m_apoRequestedMetadataLayers.end())
     238             :             {
     239          23 :                 m_apoRequestedMetadataLayers.push_back(poLayer);
     240             :             }
     241          23 :             RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
     242          23 :             return poLayer;
     243             :         }
     244             :     }
     245             : 
     246           1 :     return nullptr;
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*                           TranslateClasses()                         */
     251             : /************************************************************************/
     252             : 
     253       16958 : void OGRGMLASDataSource::TranslateClasses(OGRGMLASLayer *poParentLayer,
     254             :                                           const GMLASFeatureClass &oFC)
     255             : {
     256       16958 :     const std::vector<GMLASFeatureClass> &aoClasses = oFC.GetNestedClasses();
     257             : 
     258             :     // CPLDebug("GMLAS", "TranslateClasses(%s,%s)",
     259             :     //          oFC.GetName().c_str(), oFC.GetXPath().c_str());
     260             : 
     261       16958 :     m_apoLayers.emplace_back(std::make_unique<OGRGMLASLayer>(
     262       16958 :         this, oFC, poParentLayer, m_oConf.m_bAlwaysGenerateOGRId));
     263       16958 :     auto poLayer = m_apoLayers.back().get();
     264             : 
     265       20738 :     for (size_t i = 0; i < aoClasses.size(); ++i)
     266             :     {
     267        3780 :         TranslateClasses(poLayer, aoClasses[i]);
     268             :     }
     269       16958 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                         GMLASTopElementParser                        */
     273             : /************************************************************************/
     274             : 
     275             : class GMLASTopElementParser : public DefaultHandler
     276             : {
     277             :     std::vector<PairURIFilename> m_aoFilenames{};
     278             :     int m_nStartElementCounter = 0;
     279             :     bool m_bFinish = false;
     280             :     bool m_bFoundSWE = false;
     281             :     std::map<CPLString, CPLString> m_oMapDocNSURIToPrefix{};
     282             : 
     283             :   public:
     284         190 :     GMLASTopElementParser() = default;
     285             : 
     286             :     void Parse(const CPLString &osFilename,
     287             :                const std::shared_ptr<VSIVirtualHandle> &fp);
     288             : 
     289         155 :     const std::vector<PairURIFilename> &GetXSDs() const
     290             :     {
     291         155 :         return m_aoFilenames;
     292             :     }
     293             : 
     294         158 :     bool GetSWE() const
     295             :     {
     296         158 :         return m_bFoundSWE;
     297             :     }
     298             : 
     299         162 :     const std::map<CPLString, CPLString> &GetMapDocNSURIToPrefix() const
     300             :     {
     301         162 :         return m_oMapDocNSURIToPrefix;
     302             :     }
     303             : 
     304             :     virtual void startElement(const XMLCh *const uri,
     305             :                               const XMLCh *const localname,
     306             :                               const XMLCh *const qname,
     307             :                               const Attributes &attrs) override;
     308             : };
     309             : 
     310             : /************************************************************************/
     311             : /*                               Parse()                                */
     312             : /************************************************************************/
     313             : 
     314         158 : void GMLASTopElementParser::Parse(const CPLString &osFilename,
     315             :                                   const std::shared_ptr<VSIVirtualHandle> &fp)
     316             : {
     317             :     auto poSAXReader =
     318         316 :         std::unique_ptr<SAX2XMLReader>(XMLReaderFactory::createXMLReader());
     319             : 
     320         158 :     poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
     321         158 :     poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
     322             : 
     323         158 :     poSAXReader->setContentHandler(this);
     324         158 :     poSAXReader->setLexicalHandler(this);
     325         158 :     poSAXReader->setDTDHandler(this);
     326             : 
     327         158 :     poSAXReader->setFeature(XMLUni::fgXercesLoadSchema, false);
     328             : 
     329         316 :     GMLASErrorHandler oErrorHandler;
     330         158 :     poSAXReader->setErrorHandler(&oErrorHandler);
     331             : 
     332         316 :     GMLASInputSource oIS(osFilename, fp);
     333             : 
     334             :     try
     335             :     {
     336         316 :         XMLPScanToken oToFill;
     337         158 :         if (poSAXReader->parseFirst(oIS, oToFill))
     338             :         {
     339         316 :             while (!m_bFinish && poSAXReader->parseNext(oToFill))
     340             :             {
     341             :                 // do nothing
     342             :             }
     343             :         }
     344             :     }
     345           0 :     catch (const XMLException &toCatch)
     346             :     {
     347           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     348           0 :                  transcode(toCatch.getMessage()).c_str());
     349             :     }
     350           0 :     catch (const SAXException &toCatch)
     351             :     {
     352           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     353           0 :                  transcode(toCatch.getMessage()).c_str());
     354             :     }
     355         158 : }
     356             : 
     357             : /************************************************************************/
     358             : /*                             startElement()                           */
     359             : /************************************************************************/
     360             : 
     361         158 : void GMLASTopElementParser::startElement(const XMLCh *const /*uri*/,
     362             :                                          const XMLCh *const /*localname*/,
     363             :                                          const XMLCh *const /*qname*/,
     364             :                                          const Attributes &attrs)
     365             : {
     366         158 :     m_nStartElementCounter++;
     367             : 
     368         985 :     for (unsigned int i = 0; i < attrs.getLength(); i++)
     369             :     {
     370        1654 :         const std::string osAttrURIPrefix(transcode(attrs.getURI(i)));
     371        1654 :         const std::string osAttrLocalname(transcode(attrs.getLocalName(i)));
     372        1654 :         const std::string osAttrValue(transcode(attrs.getValue(i)));
     373             : 
     374         983 :         if (osAttrURIPrefix == szXSI_URI &&
     375         156 :             osAttrLocalname == szSCHEMA_LOCATION)
     376             :         {
     377         130 :             CPLDebug("GMLAS", "%s=%s", szSCHEMA_LOCATION, osAttrValue.c_str());
     378             : 
     379             :             const CPLStringList aosTokens(
     380         260 :                 CSLTokenizeString2(osAttrValue.c_str(), " ", 0));
     381         130 :             const int nTokens = aosTokens.size();
     382         130 :             if ((nTokens % 2) == 0)
     383             :             {
     384         343 :                 for (int j = 0; j < nTokens; j += 2)
     385             :                 {
     386         422 :                     if (!STARTS_WITH(aosTokens[j], szWFS_URI) &&
     387         209 :                         !(EQUAL(aosTokens[j], szGML_URI) ||
     388         422 :                           STARTS_WITH(aosTokens[j],
     389             :                                       (CPLString(szGML_URI) + "/").c_str())))
     390             :                     {
     391         209 :                         CPLDebug("GMLAS", "Schema to analyze: %s -> %s",
     392             :                                  aosTokens[j], aosTokens[j + 1]);
     393         209 :                         m_aoFilenames.push_back(
     394         418 :                             PairURIFilename(aosTokens[j], aosTokens[j + 1]));
     395             :                     }
     396             :                 }
     397             :             }
     398             :         }
     399         723 :         else if (osAttrURIPrefix == szXSI_URI &&
     400          26 :                  osAttrLocalname == szNO_NAMESPACE_SCHEMA_LOCATION)
     401             :         {
     402          26 :             CPLDebug("GMLAS", "%s=%s", szNO_NAMESPACE_SCHEMA_LOCATION,
     403             :                      osAttrValue.c_str());
     404          26 :             m_aoFilenames.push_back(PairURIFilename("", osAttrValue));
     405             :         }
     406         671 :         else if (osAttrURIPrefix == szXMLNS_URI && osAttrValue == szSWE_URI)
     407             :         {
     408           3 :             CPLDebug("GMLAS", "SWE namespace found");
     409           3 :             m_bFoundSWE = true;
     410             :         }
     411        1128 :         else if (osAttrURIPrefix == szXMLNS_URI && !osAttrValue.empty() &&
     412         460 :                  !osAttrLocalname.empty())
     413             :         {
     414             : #ifdef DEBUG_VERBOSE
     415             :             CPLDebug("GMLAS", "Namespace %s = %s", osAttrLocalname.c_str(),
     416             :                      osAttrValue.c_str());
     417             : #endif
     418         460 :             m_oMapDocNSURIToPrefix[osAttrValue] = osAttrLocalname;
     419             :         }
     420             :     }
     421             : 
     422         158 :     if (m_nStartElementCounter == 1)
     423         158 :         m_bFinish = true;
     424         158 : }
     425             : 
     426             : /************************************************************************/
     427             : /*                         FillOtherMetadataLayer()                     */
     428             : /************************************************************************/
     429             : 
     430         178 : void OGRGMLASDataSource::FillOtherMetadataLayer(
     431             :     GDALOpenInfo *poOpenInfo, const CPLString &osConfigFile,
     432             :     const std::vector<PairURIFilename> &aoXSDs,
     433             :     const std::set<CPLString> &oSetSchemaURLs)
     434             : {
     435             :     // 2 "secret" options just used for tests
     436         178 :     const bool bKeepRelativePathsForMetadata = CPLTestBool(
     437         178 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     438             :                              szKEEP_RELATIVE_PATHS_FOR_METADATA_OPTION, "NO"));
     439             : 
     440         178 :     const bool bExposeConfiguration = CPLTestBool(
     441         178 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     442             :                              szEXPOSE_CONFIGURATION_IN_METADATA_OPTION, "YES"));
     443             : 
     444         178 :     const bool bExposeSchemaNames = CPLTestBool(
     445         178 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     446             :                              szEXPOSE_SCHEMAS_NAME_IN_METADATA_OPTION, "YES"));
     447             : 
     448         178 :     OGRFeatureDefn *poFDefn = m_poOtherMetadataLayer->GetLayerDefn();
     449             : 
     450         178 :     if (!osConfigFile.empty() && bExposeConfiguration)
     451             :     {
     452         176 :         if (STARTS_WITH(osConfigFile, "<Configuration"))
     453             :         {
     454          85 :             OGRFeature oFeature(poFDefn);
     455          85 :             oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
     456          85 :             oFeature.SetField(szVALUE, osConfigFile.c_str());
     457          85 :             CPL_IGNORE_RET_VAL(
     458          85 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     459             :         }
     460             :         else
     461             :         {
     462             :             {
     463          91 :                 OGRFeature oFeature(poFDefn);
     464          91 :                 oFeature.SetField(szKEY, szCONFIGURATION_FILENAME);
     465          91 :                 char *pszCurDir = CPLGetCurrentDir();
     466         273 :                 if (!bKeepRelativePathsForMetadata &&
     467          91 :                     CPLIsFilenameRelative(osConfigFile) && pszCurDir != nullptr)
     468             :                 {
     469           0 :                     oFeature.SetField(
     470             :                         szVALUE,
     471           0 :                         CPLFormFilenameSafe(pszCurDir, osConfigFile, nullptr)
     472             :                             .c_str());
     473             :                 }
     474             :                 else
     475             :                 {
     476          91 :                     oFeature.SetField(szVALUE, osConfigFile.c_str());
     477             :                 }
     478          91 :                 CPLFree(pszCurDir);
     479          91 :                 CPL_IGNORE_RET_VAL(
     480          91 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     481             :             }
     482             : 
     483          91 :             GByte *pabyRet = nullptr;
     484          91 :             if (VSIIngestFile(nullptr, osConfigFile, &pabyRet, nullptr, -1))
     485             :             {
     486          91 :                 OGRFeature oFeature(poFDefn);
     487          91 :                 oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
     488          91 :                 oFeature.SetField(szVALUE, reinterpret_cast<char *>(pabyRet));
     489          91 :                 CPL_IGNORE_RET_VAL(
     490          91 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     491             :             }
     492          91 :             VSIFree(pabyRet);
     493             :         }
     494             :     }
     495             : 
     496         178 :     const char *const apszMeaningfulOptionsToStoreInMD[] = {
     497             :         szSWAP_COORDINATES_OPTION, szREMOVE_UNUSED_LAYERS_OPTION,
     498             :         szREMOVE_UNUSED_FIELDS_OPTION};
     499         712 :     for (size_t i = 0; i < CPL_ARRAYSIZE(apszMeaningfulOptionsToStoreInMD); ++i)
     500             :     {
     501         534 :         const char *pszKey = apszMeaningfulOptionsToStoreInMD[i];
     502             :         const char *pszVal =
     503         534 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszKey);
     504         534 :         if (pszVal)
     505             :         {
     506           4 :             OGRFeature oFeature(poFDefn);
     507           4 :             oFeature.SetField(szKEY, pszKey);
     508           4 :             oFeature.SetField(szVALUE, pszVal);
     509           4 :             CPL_IGNORE_RET_VAL(
     510           4 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     511             :         }
     512             :     }
     513             : 
     514         356 :     CPLString osAbsoluteGMLFilename;
     515         178 :     if (!m_osGMLFilename.empty())
     516             :     {
     517         150 :         OGRFeature oFeature(poFDefn);
     518         150 :         oFeature.SetField(szKEY, szDOCUMENT_FILENAME);
     519         150 :         char *pszCurDir = CPLGetCurrentDir();
     520         448 :         if (!bKeepRelativePathsForMetadata &&
     521         150 :             CPLIsFilenameRelative(m_osGMLFilename) && pszCurDir != nullptr)
     522             :         {
     523             :             osAbsoluteGMLFilename =
     524          79 :                 CPLFormFilenameSafe(pszCurDir, m_osGMLFilename, nullptr);
     525             :         }
     526             :         else
     527          71 :             osAbsoluteGMLFilename = m_osGMLFilename;
     528         150 :         oFeature.SetField(szVALUE, osAbsoluteGMLFilename.c_str());
     529         150 :         CPLFree(pszCurDir);
     530         150 :         CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
     531             :     }
     532             : 
     533         178 :     int nNSIdx = 1;
     534         356 :     std::set<CPLString> oSetVisitedURI;
     535         450 :     for (int i = 0; i < static_cast<int>(aoXSDs.size()); i++)
     536             :     {
     537         272 :         const CPLString osURI(aoXSDs[i].first);
     538         272 :         const std::string osXSDFilename(aoXSDs[i].second);
     539             : 
     540         272 :         oSetVisitedURI.insert(osURI);
     541             : 
     542         272 :         if (osURI == szOGRGMLAS_URI)
     543           9 :             continue;
     544             : 
     545             :         {
     546         263 :             OGRFeature oFeature(poFDefn);
     547         263 :             oFeature.SetField(szKEY, CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
     548         263 :             oFeature.SetField(szVALUE, osURI.c_str());
     549         263 :             CPL_IGNORE_RET_VAL(
     550         263 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     551             :         }
     552             : 
     553             :         {
     554         526 :             OGRFeature oFeature(poFDefn);
     555         263 :             oFeature.SetField(szKEY,
     556             :                               CPLSPrintf(szNAMESPACE_LOCATION_FMT, nNSIdx));
     557             : 
     558             :             const CPLString osAbsoluteXSDFilename(
     559         251 :                 (osXSDFilename.find("http://") != 0 &&
     560         251 :                  osXSDFilename.find("https://") != 0 &&
     561         132 :                  CPLIsFilenameRelative(osXSDFilename.c_str()))
     562         604 :                     ? CPLFormFilenameSafe(
     563         353 :                           CPLGetDirnameSafe(osAbsoluteGMLFilename).c_str(),
     564             :                           osXSDFilename.c_str(), nullptr)
     565         263 :                     : osXSDFilename);
     566         263 :             oFeature.SetField(szVALUE, osAbsoluteXSDFilename.c_str());
     567         263 :             CPL_IGNORE_RET_VAL(
     568         263 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     569             :         }
     570             : 
     571         263 :         if (m_oMapURIToPrefix.find(osURI) != m_oMapURIToPrefix.end())
     572             :         {
     573         235 :             OGRFeature oFeature(poFDefn);
     574         235 :             oFeature.SetField(szKEY,
     575             :                               CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
     576         235 :             oFeature.SetField(szVALUE, m_oMapURIToPrefix[osURI].c_str());
     577         235 :             CPL_IGNORE_RET_VAL(
     578         235 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     579             :         }
     580             : 
     581         263 :         nNSIdx++;
     582             :     }
     583             : 
     584        1111 :     for (const auto &oIter : m_oMapURIToPrefix)
     585             :     {
     586         933 :         const CPLString &osURI(oIter.first);
     587         933 :         const CPLString &osPrefix(oIter.second);
     588             : 
     589        1622 :         if (oSetVisitedURI.find(osURI) == oSetVisitedURI.end() &&
     590        1712 :             osURI != szXML_URI && osURI != szXS_URI && osURI != szXSI_URI &&
     591        1956 :             osURI != szXMLNS_URI && osURI != szOGRGMLAS_URI)
     592             :         {
     593             :             {
     594         156 :                 OGRFeature oFeature(poFDefn);
     595         156 :                 oFeature.SetField(szKEY,
     596             :                                   CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
     597         156 :                 oFeature.SetField(szVALUE, osURI.c_str());
     598         156 :                 CPL_IGNORE_RET_VAL(
     599         156 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     600             :             }
     601             : 
     602             :             {
     603         156 :                 OGRFeature oFeature(poFDefn);
     604         156 :                 oFeature.SetField(szKEY,
     605             :                                   CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
     606         156 :                 oFeature.SetField(szVALUE, osPrefix.c_str());
     607         156 :                 CPL_IGNORE_RET_VAL(
     608         156 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     609             :             }
     610             : 
     611         156 :             nNSIdx++;
     612             :         }
     613             :     }
     614             : 
     615         178 :     if (!m_osGMLVersionFound.empty())
     616             :     {
     617           5 :         OGRFeature oFeature(poFDefn);
     618           5 :         oFeature.SetField(szKEY, szGML_VERSION);
     619           5 :         oFeature.SetField(szVALUE, m_osGMLVersionFound);
     620           5 :         CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
     621             :     }
     622             : 
     623         178 :     int nSchemaIdx = 1;
     624         178 :     if (bExposeSchemaNames)
     625             :     {
     626         571 :         for (const auto &osSchemaURL : oSetSchemaURLs)
     627             :         {
     628         395 :             OGRFeature oFeature(poFDefn);
     629         395 :             oFeature.SetField(szKEY, CPLSPrintf(szSCHEMA_NAME_FMT, nSchemaIdx));
     630         395 :             oFeature.SetField(szVALUE, osSchemaURL.c_str());
     631         395 :             CPL_IGNORE_RET_VAL(
     632         395 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     633             : 
     634         395 :             nSchemaIdx++;
     635             :         }
     636             :     }
     637         178 : }
     638             : 
     639             : /************************************************************************/
     640             : /*                         BuildXSDVector()                             */
     641             : /************************************************************************/
     642             : 
     643             : std::vector<PairURIFilename>
     644          35 : OGRGMLASDataSource::BuildXSDVector(const CPLString &osXSDFilenames)
     645             : {
     646          35 :     std::vector<PairURIFilename> aoXSDs;
     647          35 :     char **papszTokens = CSLTokenizeString2(osXSDFilenames, ",", 0);
     648          35 :     char *pszCurDir = CPLGetCurrentDir();
     649          70 :     for (int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
     650             :     {
     651         104 :         if (!STARTS_WITH(papszTokens[i], "http://") &&
     652          34 :             !STARTS_WITH(papszTokens[i], "https://") &&
     653          69 :             CPLIsFilenameRelative(papszTokens[i]) && pszCurDir != nullptr)
     654             :         {
     655          29 :             aoXSDs.push_back(PairURIFilename(
     656          58 :                 "", CPLFormFilenameSafe(pszCurDir, papszTokens[i], nullptr)
     657          58 :                         .c_str()));
     658             :         }
     659             :         else
     660             :         {
     661           6 :             aoXSDs.push_back(PairURIFilename("", papszTokens[i]));
     662             :         }
     663             :     }
     664          35 :     CPLFree(pszCurDir);
     665          35 :     CSLDestroy(papszTokens);
     666          35 :     return aoXSDs;
     667             : }
     668             : 
     669             : /************************************************************************/
     670             : /*                                Open()                                */
     671             : /************************************************************************/
     672             : 
     673         195 : bool OGRGMLASDataSource::Open(GDALOpenInfo *poOpenInfo)
     674             : {
     675         195 :     m_osConfigFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     676         195 :                                           szCONFIG_FILE_OPTION, "");
     677         195 :     if (m_osConfigFile.empty())
     678             :     {
     679             :         m_osConfigFile =
     680         104 :             GMLASConfiguration::GetDefaultConfFile(m_bUnlinkConfigFileAfterUse);
     681             :     }
     682         195 :     if (m_osConfigFile.empty())
     683             :     {
     684           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     685             :                  "No configuration file found. Using hard-coded defaults");
     686           0 :         m_oConf.Finalize();
     687             :     }
     688             :     else
     689             :     {
     690         195 :         if (!m_oConf.Load(m_osConfigFile.c_str()))
     691             :         {
     692           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     693             :                      "Loading of configuration failed");
     694           2 :             return false;
     695             :         }
     696             :     }
     697             : 
     698         193 :     m_oCache.SetCacheDirectory(m_oConf.m_osXSDCacheDirectory);
     699         193 :     const bool bRefreshCache(CPLTestBool(CSLFetchNameValueDef(
     700         193 :         poOpenInfo->papszOpenOptions, szREFRESH_CACHE_OPTION, "NO")));
     701         193 :     m_oCache.SetRefreshMode(bRefreshCache);
     702         193 :     m_oCache.SetAllowDownload(m_oConf.m_bAllowRemoteSchemaDownload);
     703             : 
     704         193 :     m_oIgnoredXPathMatcher.SetRefXPaths(m_oConf.m_oMapPrefixToURIIgnoredXPaths,
     705         193 :                                         m_oConf.m_aosIgnoredXPaths);
     706             : 
     707             :     {
     708         386 :         std::vector<CPLString> oVector;
     709         300 :         for (const auto &oIter : m_oConf.m_oMapChildrenElementsConstraints)
     710         107 :             oVector.push_back(oIter.first);
     711         193 :         m_oChildrenElementsConstraintsXPathMatcher.SetRefXPaths(
     712         193 :             m_oConf.m_oMapPrefixToURITypeConstraints, oVector);
     713             :     }
     714             : 
     715         193 :     m_oForcedFlattenedXPathMatcher.SetRefXPaths(
     716         193 :         m_oConf.m_oMapPrefixToURIFlatteningRules,
     717         193 :         m_oConf.m_osForcedFlattenedXPath);
     718             : 
     719         193 :     m_oDisabledFlattenedXPathMatcher.SetRefXPaths(
     720         193 :         m_oConf.m_oMapPrefixToURIFlatteningRules,
     721         193 :         m_oConf.m_osDisabledFlattenedXPath);
     722             : 
     723             :     GMLASSchemaAnalyzer oAnalyzer(
     724         193 :         m_oIgnoredXPathMatcher, m_oChildrenElementsConstraintsXPathMatcher,
     725         193 :         m_oConf.m_oMapChildrenElementsConstraints,
     726         386 :         m_oForcedFlattenedXPathMatcher, m_oDisabledFlattenedXPathMatcher);
     727         193 :     oAnalyzer.SetUseArrays(m_oConf.m_bUseArrays);
     728         193 :     oAnalyzer.SetUseNullState(m_oConf.m_bUseNullState);
     729         193 :     oAnalyzer.SetInstantiateGMLFeaturesOnly(
     730         193 :         m_oConf.m_bInstantiateGMLFeaturesOnly);
     731         193 :     oAnalyzer.SetIdentifierMaxLength(m_oConf.m_nIdentifierMaxLength);
     732         193 :     oAnalyzer.SetCaseInsensitiveIdentifier(
     733         193 :         m_oConf.m_bCaseInsensitiveIdentifier);
     734         193 :     oAnalyzer.SetPGIdentifierLaundering(m_oConf.m_bPGIdentifierLaundering);
     735         193 :     oAnalyzer.SetMaximumFieldsForFlattening(
     736             :         m_oConf.m_nMaximumFieldsForFlattening);
     737         193 :     oAnalyzer.SetAlwaysGenerateOGRId(m_oConf.m_bAlwaysGenerateOGRId);
     738             : 
     739         193 :     m_osGMLFilename = STARTS_WITH_CI(poOpenInfo->pszFilename, szGMLAS_PREFIX)
     740         386 :                           ? CPLExpandTildeSafe(poOpenInfo->pszFilename +
     741             :                                                strlen(szGMLAS_PREFIX))
     742         193 :                           : poOpenInfo->pszFilename;
     743             : 
     744             :     CPLString osXSDFilenames =
     745         386 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, szXSD_OPTION, "");
     746             : 
     747         193 :     std::shared_ptr<VSIVirtualHandle> fpGML;
     748         193 :     if (!m_osGMLFilename.empty())
     749             :     {
     750         160 :         fpGML.reset(VSIFOpenL(m_osGMLFilename, "rb"), VSIVirtualHandleCloser{});
     751         160 :         if (fpGML == nullptr)
     752             :         {
     753           2 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     754             :                      m_osGMLFilename.c_str());
     755           2 :             return false;
     756             :         }
     757             :     }
     758          33 :     else if (osXSDFilenames.empty())
     759             :     {
     760           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     761             :                  "%s open option must be provided when no "
     762             :                  "XML data file is passed",
     763             :                  szXSD_OPTION);
     764           1 :         return false;
     765             :     }
     766             : 
     767         380 :     GMLASTopElementParser topElementParser;
     768         190 :     if (!m_osGMLFilename.empty())
     769             :     {
     770         158 :         topElementParser.Parse(m_osGMLFilename, fpGML);
     771         158 :         if (m_oConf.m_eSWEActivationMode ==
     772             :             GMLASConfiguration::SWE_ACTIVATE_IF_NAMESPACE_FOUND)
     773             :         {
     774         158 :             m_bFoundSWE = topElementParser.GetSWE();
     775             :         }
     776           0 :         else if (m_oConf.m_eSWEActivationMode ==
     777             :                  GMLASConfiguration::SWE_ACTIVATE_TRUE)
     778             :         {
     779           0 :             m_bFoundSWE = true;
     780             :         }
     781         158 :         oAnalyzer.SetMapDocNSURIToPrefix(
     782             :             topElementParser.GetMapDocNSURIToPrefix());
     783             :     }
     784         380 :     std::vector<PairURIFilename> aoXSDs;
     785         190 :     if (osXSDFilenames.empty())
     786             :     {
     787         155 :         aoXSDs = topElementParser.GetXSDs();
     788         155 :         if (aoXSDs.empty())
     789             :         {
     790             :             const std::map<std::string, std::string> mapWellKnownURIToLocation =
     791             :                 {
     792             :                     {"http://www.opengis.net/citygml/2.0",
     793             :                      "https://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd"},
     794             :                     {"http://www.opengis.net/citygml/appearance/2.0",
     795             :                      "https://schemas.opengis.net/citygml/appearance/2.0/"
     796             :                      "appearance.xsd"},
     797             :                     {"http://www.opengis.net/citygml/bridge/2.0",
     798             :                      "https://schemas.opengis.net/citygml/bridge/2.0/"
     799             :                      "bridge.xsd"},
     800             :                     {"http://www.opengis.net/citygml/building/2.0",
     801             :                      "https://schemas.opengis.net/citygml/building/2.0/"
     802             :                      "building.xsd"},
     803             :                     {
     804             :                         "http://www.opengis.net/citygml/cityfurniture/2.0",
     805             :                         "https://schemas.opengis.net/citygml/cityfurniture/2.0/"
     806             :                         "cityFurniture.xsd",
     807             :                     },
     808             :                     {"http://www.opengis.net/citygml/cityobjectgroup/2.0",
     809             :                      "https://schemas.opengis.net/citygml/cityobjectgroup/2.0/"
     810             :                      "cityObjectGroup.xsd"},
     811             :                     {"http://www.opengis.net/citygml/generics/2.0",
     812             :                      "https://schemas.opengis.net/citygml/generics/2.0/"
     813             :                      "generics.xsd"},
     814             :                     {"http://www.opengis.net/citygml/landuse/2.0",
     815             :                      "https://schemas.opengis.net/citygml/landuse/2.0/"
     816             :                      "landUse.xsd"},
     817             :                     {"http://www.opengis.net/citygml/relief/2.0",
     818             :                      "https://schemas.opengis.net/citygml/relief/2.0/"
     819             :                      "relief.xsd"},
     820             :                     // { "http://www.opengis.net/citygml/textures/2.0",  } ,
     821             :                     {"http://www.opengis.net/citygml/transportation/2.0",
     822             :                      "https://schemas.opengis.net/citygml/transportation/2.0/"
     823             :                      "transportation.xsd"},
     824             :                     {"http://www.opengis.net/citygml/tunnel/2.0",
     825             :                      "https://schemas.opengis.net/citygml/tunnel/2.0/"
     826             :                      "tunnel.xsd"},
     827             :                     {"http://www.opengis.net/citygml/vegetation/2.0",
     828             :                      "https://schemas.opengis.net/citygml/vegetation/2.0/"
     829             :                      "vegetation.xsd"},
     830             :                     {"http://www.opengis.net/citygml/waterbody/2.0",
     831             :                      "https://schemas.opengis.net/citygml/waterbody/2.0/"
     832             :                      "waterBody.xsd"},
     833             :                     {"http://www.w3.org/1999/xlink",
     834             :                      "https://www.w3.org/1999/xlink.xsd"},
     835             :                     {"urn:oasis:names:tc:ciq:xsdschema:xAL:2.0",
     836             :                      "https://schemas.opengis.net/citygml/xAL/xAL.xsd"},
     837          36 :                 };
     838             : 
     839           2 :             bool cityGML2Found = false;
     840          38 :             for (const auto &[uri, prefix] :
     841          40 :                  topElementParser.GetMapDocNSURIToPrefix())
     842             :             {
     843          19 :                 if (uri == "http://www.opengis.net/citygml/2.0")
     844           1 :                     cityGML2Found = true;
     845             :             }
     846             : 
     847          38 :             for (const auto &[uri, prefix] :
     848          40 :                  topElementParser.GetMapDocNSURIToPrefix())
     849             :             {
     850          19 :                 const auto iter = mapWellKnownURIToLocation.find(uri);
     851          19 :                 if (iter != mapWellKnownURIToLocation.end())
     852             :                 {
     853          15 :                     aoXSDs.push_back(PairURIFilename(uri, iter->second));
     854             :                 }
     855           4 :                 else if (cityGML2Found && uri == "http://www.opengis.net/gml")
     856             :                 {
     857           1 :                     aoXSDs.push_back(PairURIFilename(
     858             :                         uri,
     859             :                         "https://schemas.opengis.net/gml/3.1.1/base/gml.xsd"));
     860             :                 }
     861           3 :                 else if (uri != "http://www.w3.org/2001/XMLSchema-instance")
     862             :                 {
     863           2 :                     CPLDebug("GMLAS",
     864             :                              "Could not find schema location for %s(%s)",
     865             :                              prefix.c_str(), uri.c_str());
     866             :                 }
     867             :             }
     868             : 
     869           2 :             m_aoXSDsManuallyPassed = aoXSDs;
     870             :         }
     871             :     }
     872             :     else
     873             :     {
     874          35 :         aoXSDs = BuildXSDVector(osXSDFilenames);
     875             :     }
     876         190 :     if (fpGML)
     877             :     {
     878             :         m_osHash =
     879         158 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "HASH", "");
     880         158 :         if (m_osHash.empty())
     881             :         {
     882         152 :             fpGML->Seek(0, SEEK_SET);
     883         304 :             std::string osBuffer;
     884         152 :             osBuffer.resize(8192);
     885         152 :             size_t nRead = fpGML->Read(&osBuffer[0], 1, 8192);
     886         152 :             osBuffer.resize(nRead);
     887         152 :             size_t nPos = osBuffer.find("timeStamp=\"");
     888         152 :             if (nPos != std::string::npos)
     889             :             {
     890             :                 size_t nPos2 =
     891           6 :                     osBuffer.find('"', nPos + strlen("timeStamp=\""));
     892           6 :                 if (nPos2 != std::string::npos)
     893           6 :                     osBuffer.replace(nPos, nPos2 - nPos + 1, nPos2 - nPos + 1,
     894           6 :                                      ' ');
     895             :             }
     896             :             CPL_SHA256Context ctxt;
     897         152 :             CPL_SHA256Init(&ctxt);
     898         152 :             CPL_SHA256Update(&ctxt, osBuffer.data(), osBuffer.size());
     899             : 
     900             :             VSIStatBufL sStat;
     901         152 :             if (VSIStatL(m_osGMLFilename, &sStat) == 0)
     902             :             {
     903         152 :                 m_nFileSize = sStat.st_size;
     904         152 :                 GUInt64 nFileSizeLittleEndian =
     905         152 :                     static_cast<GUInt64>(sStat.st_size);
     906         152 :                 CPL_LSBPTR64(&nFileSizeLittleEndian);
     907         152 :                 CPL_SHA256Update(&ctxt, &nFileSizeLittleEndian,
     908             :                                  sizeof(nFileSizeLittleEndian));
     909             :             }
     910             : 
     911             :             GByte abyHash[CPL_SHA256_HASH_SIZE];
     912         152 :             CPL_SHA256Final(&ctxt, abyHash);
     913             :             // Half of the hash should be enough for our purpose
     914         152 :             char *pszHash = CPLBinaryToHex(CPL_SHA256_HASH_SIZE / 2, abyHash);
     915         152 :             m_osHash = pszHash;
     916         152 :             CPLFree(pszHash);
     917             :         }
     918             : 
     919         158 :         fpGML->Seek(0, SEEK_SET);
     920         158 :         PushUnusedGMLFilePointer(fpGML);
     921             :     }
     922             : 
     923         190 :     if (aoXSDs.empty())
     924             :     {
     925           1 :         if (osXSDFilenames.empty())
     926             :         {
     927           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     928             :                      "No schema locations found when analyzing data file: "
     929             :                      "%s open option must be provided",
     930             :                      szXSD_OPTION);
     931             :         }
     932             :         else
     933             :         {
     934           0 :             CPLError(CE_Failure, CPLE_AppDefined, "No schema locations found");
     935             :         }
     936           1 :         return false;
     937             :     }
     938             : 
     939         378 :     m_bSchemaFullChecking = CPLFetchBool(poOpenInfo->papszOpenOptions,
     940             :                                          szSCHEMA_FULL_CHECKING_OPTION,
     941         189 :                                          m_oConf.m_bSchemaFullChecking);
     942             : 
     943         378 :     m_bHandleMultipleImports = CPLFetchBool(poOpenInfo->papszOpenOptions,
     944             :                                             szHANDLE_MULTIPLE_IMPORTS_OPTION,
     945         189 :                                             m_oConf.m_bHandleMultipleImports);
     946             : 
     947         378 :     bool bRet = oAnalyzer.Analyze(
     948         567 :         m_oCache, CPLGetDirnameSafe(m_osGMLFilename).c_str(), aoXSDs,
     949         189 :         m_bSchemaFullChecking, m_bHandleMultipleImports);
     950         189 :     if (!bRet)
     951             :     {
     952          11 :         return false;
     953             :     }
     954             : 
     955         178 :     if (!osXSDFilenames.empty())
     956          31 :         m_aoXSDsManuallyPassed = aoXSDs;
     957             : 
     958         178 :     m_oMapURIToPrefix = oAnalyzer.GetMapURIToPrefix();
     959             : 
     960         178 :     m_osGMLVersionFound = oAnalyzer.GetGMLVersionFound();
     961             : 
     962         178 :     const std::set<CPLString> &oSetSchemaURLs = oAnalyzer.GetSchemaURLS();
     963             : 
     964         178 :     FillOtherMetadataLayer(poOpenInfo, m_osConfigFile, aoXSDs, oSetSchemaURLs);
     965             : 
     966         178 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions,
     967             :                      szEXPOSE_METADATA_LAYERS_OPTION,
     968         178 :                      m_oConf.m_bExposeMetadataLayers))
     969             :     {
     970          15 :         m_apoRequestedMetadataLayers.push_back(m_poFieldsMetadataLayer.get());
     971          15 :         m_apoRequestedMetadataLayers.push_back(m_poLayersMetadataLayer.get());
     972          15 :         m_apoRequestedMetadataLayers.push_back(m_poRelationshipsLayer.get());
     973          15 :         m_apoRequestedMetadataLayers.push_back(m_poOtherMetadataLayer.get());
     974             :     }
     975             : 
     976         356 :     const char *pszSwapCoordinates = CSLFetchNameValueDef(
     977         178 :         poOpenInfo->papszOpenOptions, szSWAP_COORDINATES_OPTION, "AUTO");
     978         178 :     if (EQUAL(pszSwapCoordinates, "AUTO"))
     979             :     {
     980         176 :         m_eSwapCoordinates = GMLAS_SWAP_AUTO;
     981             :     }
     982           2 :     else if (CPLTestBool(pszSwapCoordinates))
     983             :     {
     984           1 :         m_eSwapCoordinates = GMLAS_SWAP_YES;
     985             :     }
     986             :     else
     987             :     {
     988           1 :         m_eSwapCoordinates = GMLAS_SWAP_NO;
     989             :     }
     990             : 
     991         178 :     const std::vector<GMLASFeatureClass> &aoClasses = oAnalyzer.GetClasses();
     992             : 
     993             :     // First "standard" tables
     994       13356 :     for (auto &oClass : aoClasses)
     995             :     {
     996       13178 :         if (oClass.GetParentXPath().empty())
     997        3321 :             TranslateClasses(nullptr, oClass);
     998             :     }
     999             :     // Then junction tables
    1000       13356 :     for (auto &oClass : aoClasses)
    1001             :     {
    1002       13178 :         if (!oClass.GetParentXPath().empty())
    1003        9857 :             TranslateClasses(nullptr, oClass);
    1004             :     }
    1005             : 
    1006             :     // And now do initialization since we need to have instantiated everything
    1007             :     // to be able to do cross-layer links
    1008       17136 :     for (auto &poLayer : m_apoLayers)
    1009             :     {
    1010       16958 :         poLayer->PostInit(m_oConf.m_bIncludeGeometryXML);
    1011             :     }
    1012         178 :     m_bLayerInitFinished = true;
    1013             : 
    1014             :     // Do optional validation
    1015         356 :     m_bValidate = CPLFetchBool(poOpenInfo->papszOpenOptions, szVALIDATE_OPTION,
    1016         178 :                                m_oConf.m_bValidate);
    1017             : 
    1018         356 :     m_bRemoveUnusedLayers = CPLFetchBool(poOpenInfo->papszOpenOptions,
    1019             :                                          szREMOVE_UNUSED_LAYERS_OPTION,
    1020         178 :                                          m_oConf.m_bRemoveUnusedLayers);
    1021             : 
    1022         356 :     m_bRemoveUnusedFields = CPLFetchBool(poOpenInfo->papszOpenOptions,
    1023             :                                          szREMOVE_UNUSED_FIELDS_OPTION,
    1024         178 :                                          m_oConf.m_bRemoveUnusedFields);
    1025             : 
    1026         178 :     m_oXLinkResolver.SetConf(m_oConf.m_oXLinkResolution);
    1027         178 :     m_oXLinkResolver.SetRefreshMode(bRefreshCache);
    1028             : 
    1029         178 :     if (m_bValidate || m_bRemoveUnusedLayers ||
    1030         126 :         (m_bFoundSWE &&
    1031           2 :          (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
    1032             :     {
    1033          54 :         CPLErrorReset();
    1034          54 :         RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
    1035         162 :         if (CPLFetchBool(poOpenInfo->papszOpenOptions,
    1036             :                          szFAIL_IF_VALIDATION_ERROR_OPTION,
    1037          96 :                          m_oConf.m_bFailIfValidationError) &&
    1038          42 :             CPLGetLastErrorType() != CE_None)
    1039             :         {
    1040           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1041             :                      "Validation errors encountered");
    1042           2 :             return false;
    1043             :         }
    1044             :     }
    1045         176 :     if (CPLGetLastErrorType() == CE_Failure)
    1046           1 :         CPLErrorReset();
    1047             : 
    1048         176 :     return true;
    1049             : }
    1050             : 
    1051             : /************************************************************************/
    1052             : /*                           TestCapability()                           */
    1053             : /************************************************************************/
    1054             : 
    1055          96 : int OGRGMLASDataSource::TestCapability(const char *pszCap)
    1056             : {
    1057          96 :     return EQUAL(pszCap, ODsCRandomLayerRead);
    1058             : }
    1059             : 
    1060             : /************************************************************************/
    1061             : /*                           CreateReader()                             */
    1062             : /************************************************************************/
    1063             : 
    1064             : GMLASReader *
    1065        1282 : OGRGMLASDataSource::CreateReader(std::shared_ptr<VSIVirtualHandle> &fpGML,
    1066             :                                  GDALProgressFunc pfnProgress,
    1067             :                                  void *pProgressData)
    1068             : {
    1069        1282 :     if (fpGML == nullptr)
    1070             :     {
    1071             :         // Try recycling an already opened and unused file pointer
    1072        1017 :         fpGML = PopUnusedGMLFilePointer();
    1073        1017 :         if (fpGML == nullptr)
    1074           7 :             fpGML.reset(VSIFOpenL(GetGMLFilename(), "rb"),
    1075             :                         VSIVirtualHandleCloser{});
    1076        1017 :         if (fpGML == nullptr)
    1077           2 :             return nullptr;
    1078             :     }
    1079             : 
    1080             :     auto poReader = std::make_unique<GMLASReader>(
    1081        2560 :         GetCache(), GetIgnoredXPathMatcher(), m_oXLinkResolver);
    1082        2560 :     poReader->Init(GetGMLFilename(), fpGML, GetMapURIToPrefix(), GetLayers(),
    1083        1280 :                    false, std::vector<PairURIFilename>(), m_bSchemaFullChecking,
    1084        1280 :                    m_bHandleMultipleImports);
    1085             : 
    1086        1280 :     poReader->SetSwapCoordinates(GetSwapCoordinates());
    1087             : 
    1088        1280 :     poReader->SetFileSize(m_nFileSize);
    1089             : 
    1090        1280 :     if (!RunFirstPassIfNeeded(poReader.get(), pfnProgress, pProgressData))
    1091             :     {
    1092           0 :         return nullptr;
    1093             :     }
    1094             : 
    1095        1280 :     poReader->SetMapIgnoredXPathToWarn(GetMapIgnoredXPathToWarn());
    1096             : 
    1097        1280 :     poReader->SetHash(m_osHash);
    1098             : 
    1099        1280 :     return poReader.release();
    1100             : }
    1101             : 
    1102             : /************************************************************************/
    1103             : /*                            ResetReading()                            */
    1104             : /************************************************************************/
    1105             : 
    1106           2 : void OGRGMLASDataSource::ResetReading()
    1107             : {
    1108           2 :     m_poReader.reset();
    1109           6 :     for (auto *poLayer : m_apoRequestedMetadataLayers)
    1110           4 :         poLayer->ResetReading();
    1111           2 :     m_bEndOfReaderLayers = false;
    1112           2 :     m_nCurMetadataLayerIdx = -1;
    1113           2 : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                           GetNextFeature()                           */
    1117             : /************************************************************************/
    1118             : 
    1119        2774 : OGRFeature *OGRGMLASDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
    1120             :                                                double *pdfProgressPct,
    1121             :                                                GDALProgressFunc pfnProgress,
    1122             :                                                void *pProgressData)
    1123             : {
    1124        2774 :     if (m_bEndOfReaderLayers)
    1125             :     {
    1126        3934 :         if (m_nCurMetadataLayerIdx >= 0 &&
    1127        1963 :             m_nCurMetadataLayerIdx <
    1128        1963 :                 static_cast<int>(m_apoRequestedMetadataLayers.size()))
    1129             :         {
    1130             :             while (true)
    1131             :             {
    1132             :                 OGRLayer *poLayer =
    1133        2002 :                     m_apoRequestedMetadataLayers[m_nCurMetadataLayerIdx];
    1134        2002 :                 OGRFeature *poFeature = poLayer->GetNextFeature();
    1135        2002 :                 if (poFeature != nullptr)
    1136             :                 {
    1137        1945 :                     if (pdfProgressPct != nullptr)
    1138           0 :                         *pdfProgressPct = 1.0;
    1139        1945 :                     if (ppoBelongingLayer != nullptr)
    1140        1945 :                         *ppoBelongingLayer = poLayer;
    1141        1945 :                     return poFeature;
    1142             :                 }
    1143          57 :                 if (m_nCurMetadataLayerIdx + 1 <
    1144          57 :                     static_cast<int>(m_apoRequestedMetadataLayers.size()))
    1145             :                 {
    1146          39 :                     m_nCurMetadataLayerIdx++;
    1147             :                 }
    1148             :                 else
    1149             :                 {
    1150          18 :                     m_nCurMetadataLayerIdx = -1;
    1151          18 :                     break;
    1152             :                 }
    1153          39 :             }
    1154             :         }
    1155             : 
    1156          26 :         if (pdfProgressPct != nullptr)
    1157           0 :             *pdfProgressPct = 1.0;
    1158          26 :         if (ppoBelongingLayer != nullptr)
    1159          26 :             *ppoBelongingLayer = nullptr;
    1160          26 :         return nullptr;
    1161             :     }
    1162             : 
    1163         803 :     const double dfInitialScanRatio = 0.1;
    1164         803 :     if (m_poReader == nullptr)
    1165             :     {
    1166          23 :         void *pScaledProgress = GDALCreateScaledProgress(
    1167             :             0.0, dfInitialScanRatio, pfnProgress, pProgressData);
    1168             : 
    1169          46 :         m_poReader.reset(CreateReader(
    1170          23 :             m_fpGMLParser, pScaledProgress ? GDALScaledProgress : nullptr,
    1171             :             pScaledProgress));
    1172             : 
    1173          23 :         GDALDestroyScaledProgress(pScaledProgress);
    1174             : 
    1175          23 :         if (m_poReader == nullptr)
    1176             :         {
    1177           1 :             if (pdfProgressPct != nullptr)
    1178           0 :                 *pdfProgressPct = 1.0;
    1179           1 :             if (ppoBelongingLayer != nullptr)
    1180           1 :                 *ppoBelongingLayer = nullptr;
    1181           1 :             m_bEndOfReaderLayers = true;
    1182           1 :             if (!m_apoRequestedMetadataLayers.empty())
    1183             :             {
    1184           1 :                 m_nCurMetadataLayerIdx = 0;
    1185           1 :                 return GetNextFeature(ppoBelongingLayer, pdfProgressPct,
    1186           1 :                                       pfnProgress, pProgressData);
    1187             :             }
    1188             :             else
    1189             :             {
    1190           0 :                 return nullptr;
    1191             :             }
    1192             :         }
    1193             :     }
    1194             : 
    1195         802 :     void *pScaledProgress = GDALCreateScaledProgress(
    1196             :         dfInitialScanRatio, 1.0, pfnProgress, pProgressData);
    1197             : 
    1198             :     while (true)
    1199             :     {
    1200         802 :         OGRGMLASLayer *poBelongingLayer = nullptr;
    1201             :         auto poFeature = std::unique_ptr<OGRFeature>(m_poReader->GetNextFeature(
    1202             :             &poBelongingLayer, pScaledProgress ? GDALScaledProgress : nullptr,
    1203         802 :             pScaledProgress));
    1204        1582 :         if (poFeature == nullptr ||
    1205         780 :             poBelongingLayer->EvaluateFilter(poFeature.get()))
    1206             :         {
    1207         802 :             if (ppoBelongingLayer != nullptr)
    1208         802 :                 *ppoBelongingLayer = poBelongingLayer;
    1209         802 :             if (pdfProgressPct != nullptr)
    1210             :             {
    1211          60 :                 const vsi_l_offset nOffset = m_fpGMLParser->Tell();
    1212          60 :                 if (nOffset == m_nFileSize)
    1213          60 :                     *pdfProgressPct = 1.0;
    1214             :                 else
    1215           0 :                     *pdfProgressPct =
    1216           0 :                         dfInitialScanRatio +
    1217           0 :                         (1.0 - dfInitialScanRatio) * nOffset / m_nFileSize;
    1218             :             }
    1219         802 :             GDALDestroyScaledProgress(pScaledProgress);
    1220         802 :             if (poFeature == nullptr)
    1221             :             {
    1222          22 :                 m_bEndOfReaderLayers = true;
    1223          22 :                 if (!m_apoRequestedMetadataLayers.empty())
    1224             :                 {
    1225          17 :                     m_nCurMetadataLayerIdx = 0;
    1226          17 :                     return GetNextFeature(ppoBelongingLayer, pdfProgressPct,
    1227          17 :                                           pfnProgress, pProgressData);
    1228             :                 }
    1229             :                 else
    1230             :                 {
    1231           5 :                     return nullptr;
    1232             :                 }
    1233             :             }
    1234             :             else
    1235         780 :                 return poFeature.release();
    1236             :         }
    1237           0 :     }
    1238             : }
    1239             : 
    1240             : /************************************************************************/
    1241             : /*                          GetLayerByXPath()                           */
    1242             : /************************************************************************/
    1243             : 
    1244       37927 : OGRGMLASLayer *OGRGMLASDataSource::GetLayerByXPath(const CPLString &osXPath)
    1245             : {
    1246     7025100 :     for (auto &poLayer : m_apoLayers)
    1247             :     {
    1248     7023940 :         if (poLayer->GetFeatureClass().GetXPath() == osXPath)
    1249             :         {
    1250       36764 :             return poLayer.get();
    1251             :         }
    1252             :     }
    1253        1163 :     return nullptr;
    1254             : }
    1255             : 
    1256             : /************************************************************************/
    1257             : /*                       PushUnusedGMLFilePointer()                     */
    1258             : /************************************************************************/
    1259             : 
    1260        1179 : void OGRGMLASDataSource::PushUnusedGMLFilePointer(
    1261             :     std::shared_ptr<VSIVirtualHandle> &fpGML)
    1262             : {
    1263        1179 :     if (m_fpGML == nullptr)
    1264             :     {
    1265        1108 :         std::swap(m_fpGML, fpGML);
    1266             :     }
    1267             :     else
    1268             :     {
    1269          71 :         fpGML.reset();
    1270             :     }
    1271        1179 : }
    1272             : 
    1273             : /************************************************************************/
    1274             : /*                        PopUnusedGMLFilePointer()                     */
    1275             : /************************************************************************/
    1276             : 
    1277        1017 : std::shared_ptr<VSIVirtualHandle> OGRGMLASDataSource::PopUnusedGMLFilePointer()
    1278             : {
    1279        1017 :     std::shared_ptr<VSIVirtualHandle> fpGML;
    1280        1017 :     std::swap(fpGML, m_fpGML);
    1281        1017 :     return fpGML;
    1282             : }
    1283             : 
    1284             : /************************************************************************/
    1285             : /*                    InitReaderWithFirstPassElements()                 */
    1286             : /************************************************************************/
    1287             : 
    1288        1490 : void OGRGMLASDataSource::InitReaderWithFirstPassElements(GMLASReader *poReader)
    1289             : {
    1290        1490 :     if (poReader != nullptr)
    1291             :     {
    1292        1267 :         poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
    1293        1267 :         poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
    1294        1271 :         poReader->SetProcessDataRecord(m_bFoundSWE &&
    1295           4 :                                        m_oConf.m_bSWEProcessDataRecord);
    1296        1267 :         poReader->SetSWEDataArrayLayersRef(m_apoSWEDataArrayLayersRef);
    1297        1267 :         poReader->SetMapElementIdToLayer(m_oMapElementIdToLayer);
    1298        1267 :         poReader->SetMapElementIdToPKID(m_oMapElementIdToPKID);
    1299        1267 :         poReader->SetDefaultSrsDimension(m_nDefaultSrsDimension);
    1300             :     }
    1301        1490 : }
    1302             : 
    1303             : /************************************************************************/
    1304             : /*                          RunFirstPassIfNeeded()                      */
    1305             : /************************************************************************/
    1306             : 
    1307        1510 : bool OGRGMLASDataSource::RunFirstPassIfNeeded(GMLASReader *poReader,
    1308             :                                               GDALProgressFunc pfnProgress,
    1309             :                                               void *pProgressData)
    1310             : {
    1311        1510 :     if (m_bFirstPassDone)
    1312             :     {
    1313        1378 :         InitReaderWithFirstPassElements(poReader);
    1314        1378 :         return true;
    1315             :     }
    1316             : 
    1317         132 :     m_bFirstPassDone = true;
    1318             : 
    1319             :     // Determine if we have geometry fields in any layer
    1320             :     // If so, do an initial pass to determine the SRS of those geometry fields.
    1321         132 :     bool bHasGeomFields = false;
    1322       14323 :     for (auto &poLayer : m_apoLayers)
    1323             :     {
    1324       14212 :         poLayer->SetLayerDefnFinalized(true);
    1325       14212 :         if (poLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
    1326             :         {
    1327          21 :             bHasGeomFields = true;
    1328          21 :             break;
    1329             :         }
    1330             :     }
    1331             : 
    1332         132 :     bool bSuccess = true;
    1333             :     const bool bHasURLSpecificRules =
    1334         132 :         !m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
    1335         111 :     if (bHasGeomFields || m_bValidate || m_bRemoveUnusedLayers ||
    1336          61 :         m_bRemoveUnusedFields || bHasURLSpecificRules ||
    1337         256 :         m_oXLinkResolver.GetConf().m_bResolveInternalXLinks ||
    1338          13 :         (m_bFoundSWE &&
    1339           0 :          (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
    1340             :     {
    1341         119 :         bool bJustOpenedFiled = false;
    1342           0 :         std::shared_ptr<VSIVirtualHandle> fp;
    1343         119 :         if (poReader)
    1344          43 :             fp = poReader->GetFP();
    1345             :         else
    1346             :         {
    1347          76 :             fp.reset(VSIFOpenL(GetGMLFilename(), "rb"),
    1348             :                      VSIVirtualHandleCloser{});
    1349          76 :             if (fp == nullptr)
    1350             :             {
    1351           7 :                 return false;
    1352             :             }
    1353          69 :             bJustOpenedFiled = true;
    1354             :         }
    1355             : 
    1356             :         auto poReaderFirstPass = std::make_unique<GMLASReader>(
    1357         224 :             m_oCache, m_oIgnoredXPathMatcher, m_oXLinkResolver);
    1358         224 :         poReaderFirstPass->Init(GetGMLFilename(), fp, GetMapURIToPrefix(),
    1359         112 :                                 GetLayers(), m_bValidate,
    1360         112 :                                 m_aoXSDsManuallyPassed, m_bSchemaFullChecking,
    1361         112 :                                 m_bHandleMultipleImports);
    1362             : 
    1363         224 :         poReaderFirstPass->SetProcessDataRecord(
    1364         112 :             m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
    1365             : 
    1366         112 :         poReaderFirstPass->SetFileSize(m_nFileSize);
    1367             : 
    1368         112 :         poReaderFirstPass->SetMapIgnoredXPathToWarn(
    1369         112 :             m_oConf.m_oMapIgnoredXPathToWarn);
    1370             : 
    1371         112 :         poReaderFirstPass->SetHash(m_osHash);
    1372             : 
    1373             :         // No need to warn afterwards
    1374         112 :         m_oConf.m_oMapIgnoredXPathToWarn.clear();
    1375             : 
    1376         224 :         std::set<CPLString> aoSetRemovedLayerNames;
    1377         112 :         bSuccess = poReaderFirstPass->RunFirstPass(
    1378         112 :             pfnProgress, pProgressData, m_bRemoveUnusedLayers,
    1379         112 :             m_bRemoveUnusedFields,
    1380         112 :             m_bFoundSWE && m_oConf.m_bSWEProcessDataArray,
    1381             :             m_poFieldsMetadataLayer.get(), m_poLayersMetadataLayer.get(),
    1382             :             m_poRelationshipsLayer.get(), aoSetRemovedLayerNames);
    1383             : 
    1384             :         std::vector<std::unique_ptr<OGRGMLASLayer>> apoSWEDataArrayLayers =
    1385         224 :             poReaderFirstPass->StealSWEDataArrayLayersOwned();
    1386         115 :         for (auto &poLayer : apoSWEDataArrayLayers)
    1387             :         {
    1388           3 :             poLayer->SetDataSource(this);
    1389           3 :             m_apoSWEDataArrayLayersRef.push_back(poLayer.get());
    1390           3 :             m_apoLayers.emplace_back(std::move(poLayer));
    1391             :         }
    1392             : 
    1393             :         // If we have removed layers, we also need to cleanup our special
    1394             :         // metadata layers
    1395         112 :         if (!aoSetRemovedLayerNames.empty())
    1396             :         {
    1397             :             // Removing features while iterating works here given the layers
    1398             :             // are MEM layers
    1399           1 :             m_poLayersMetadataLayer->ResetReading();
    1400           5 :             for (auto &poFeature : *m_poLayersMetadataLayer)
    1401             :             {
    1402             :                 const char *pszLayerName =
    1403           4 :                     poFeature->GetFieldAsString(szLAYER_NAME);
    1404           4 :                 if (aoSetRemovedLayerNames.find(pszLayerName) !=
    1405           8 :                     aoSetRemovedLayerNames.end())
    1406             :                 {
    1407           6 :                     CPL_IGNORE_RET_VAL(m_poLayersMetadataLayer->DeleteFeature(
    1408           3 :                         poFeature->GetFID()));
    1409             :                 }
    1410             :             }
    1411           1 :             m_poLayersMetadataLayer->ResetReading();
    1412             : 
    1413           1 :             m_poFieldsMetadataLayer->ResetReading();
    1414          14 :             for (auto &poFeature : *m_poFieldsMetadataLayer)
    1415             :             {
    1416             :                 const char *pszLayerName =
    1417          13 :                     poFeature->GetFieldAsString(szLAYER_NAME);
    1418             :                 const char *pszRelatedLayerName =
    1419          13 :                     poFeature->GetFieldAsString(szFIELD_RELATED_LAYER);
    1420          26 :                 if (aoSetRemovedLayerNames.find(pszLayerName) !=
    1421          47 :                         aoSetRemovedLayerNames.end() ||
    1422          21 :                     aoSetRemovedLayerNames.find(pszRelatedLayerName) !=
    1423          21 :                         aoSetRemovedLayerNames.end())
    1424             :                 {
    1425          12 :                     CPL_IGNORE_RET_VAL(m_poFieldsMetadataLayer->DeleteFeature(
    1426           6 :                         poFeature->GetFID()));
    1427             :                 }
    1428             :             }
    1429           1 :             m_poFieldsMetadataLayer->ResetReading();
    1430             : 
    1431           1 :             m_poRelationshipsLayer->ResetReading();
    1432           2 :             for (auto &poFeature : *m_poRelationshipsLayer)
    1433             :             {
    1434             :                 const char *pszParentLayerName =
    1435           1 :                     poFeature->GetFieldAsString(szPARENT_LAYER);
    1436             :                 const char *pszChildLayerName =
    1437           1 :                     poFeature->GetFieldAsString(szCHILD_LAYER);
    1438           2 :                 if (aoSetRemovedLayerNames.find(pszParentLayerName) !=
    1439           4 :                         aoSetRemovedLayerNames.end() ||
    1440           2 :                     aoSetRemovedLayerNames.find(pszChildLayerName) !=
    1441           2 :                         aoSetRemovedLayerNames.end())
    1442             :                 {
    1443           2 :                     CPL_IGNORE_RET_VAL(m_poRelationshipsLayer->DeleteFeature(
    1444           1 :                         poFeature->GetFID()));
    1445             :                 }
    1446             :             }
    1447           1 :             m_poRelationshipsLayer->ResetReading();
    1448             :         }
    1449             : 
    1450             :         // Store  maps to reinject them in real readers
    1451             :         m_oMapSRSNameToInvertedAxis =
    1452         112 :             poReaderFirstPass->GetMapSRSNameToInvertedAxis();
    1453             :         m_oMapGeomFieldDefnToSRSName =
    1454         112 :             poReaderFirstPass->GetMapGeomFieldDefnToSRSName();
    1455             : 
    1456         112 :         m_oMapElementIdToLayer = poReaderFirstPass->GetMapElementIdToLayer();
    1457         112 :         m_oMapElementIdToPKID = poReaderFirstPass->GetMapElementIdToPKID();
    1458         112 :         m_nDefaultSrsDimension = poReaderFirstPass->GetDefaultSrsDimension();
    1459             : 
    1460         112 :         poReaderFirstPass.reset();
    1461             : 
    1462         112 :         fp->Seek(0, SEEK_SET);
    1463         112 :         if (bJustOpenedFiled)
    1464          69 :             PushUnusedGMLFilePointer(fp);
    1465             : 
    1466         112 :         InitReaderWithFirstPassElements(poReader);
    1467             :     }
    1468             : 
    1469         125 :     return bSuccess;
    1470             : }

Generated by: LCOV version 1.14