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

Generated by: LCOV version 1.14