LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/nas - nasreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 311 465 66.9 %
Date: 2024-05-03 15:49:35 Functions: 25 30 83.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NAS Reader
       4             :  * Purpose:  Implementation of NASReader class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam
       9             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      22             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "gmlreaderp.h"
      31             : #include "gmlreader.h"
      32             : 
      33             : #include <algorithm>
      34             : 
      35             : #include "cpl_conv.h"
      36             : #include "cpl_error.h"
      37             : #include "cpl_multiproc.h"
      38             : #include "cpl_string.h"
      39             : #include "gmlutils.h"
      40             : #include "ogr_geometry.h"
      41             : 
      42             : /************************************************************************/
      43             : /* ==================================================================== */
      44             : /*                  With XERCES Library                                 */
      45             : /* ==================================================================== */
      46             : /************************************************************************/
      47             : 
      48             : #include "nasreaderp.h"
      49             : 
      50             : /************************************************************************/
      51             : /*                          CreateNASReader()                           */
      52             : /************************************************************************/
      53             : 
      54           3 : IGMLReader *CreateNASReader()
      55             : 
      56             : {
      57           3 :     return new NASReader();
      58             : }
      59             : 
      60             : /************************************************************************/
      61             : /*                             NASReader()                              */
      62             : /************************************************************************/
      63             : 
      64           3 : NASReader::NASReader()
      65             :     : m_bClassListLocked(false), m_nClassCount(0), m_papoClass(nullptr),
      66             :       m_pszFilename(nullptr), m_poNASHandler(nullptr), m_poSAXReader(nullptr),
      67             :       m_bReadStarted(false), m_bXercesInitialized(false), m_poState(nullptr),
      68             :       m_poCompleteFeature(nullptr), m_fp(nullptr), m_GMLInputSource(nullptr),
      69           3 :       m_pszFilteredClassName(nullptr)
      70             : {
      71           3 : }
      72             : 
      73             : /************************************************************************/
      74             : /*                             ~NASReader()                             */
      75             : /************************************************************************/
      76             : 
      77           6 : NASReader::~NASReader()
      78             : 
      79             : {
      80           3 :     NASReader::ClearClasses();
      81             : 
      82           3 :     CPLFree(m_pszFilename);
      83             : 
      84           3 :     CleanupParser();
      85             : 
      86           3 :     if (m_fp)
      87           3 :         VSIFCloseL(m_fp);
      88             : 
      89           3 :     if (m_bXercesInitialized)
      90           3 :         OGRDeinitializeXerces();
      91             : 
      92           3 :     CPLFree(m_pszFilteredClassName);
      93           6 : }
      94             : 
      95             : /************************************************************************/
      96             : /*                          SetSourceFile()                             */
      97             : /************************************************************************/
      98             : 
      99           3 : void NASReader::SetSourceFile(const char *pszFilename)
     100             : 
     101             : {
     102           3 :     CPLFree(m_pszFilename);
     103           3 :     m_pszFilename = CPLStrdup(pszFilename);
     104           3 : }
     105             : 
     106             : /************************************************************************/
     107             : /*                       GetSourceFileName()                            */
     108             : /************************************************************************/
     109             : 
     110           0 : const char *NASReader::GetSourceFileName()
     111             : {
     112           0 :     return m_pszFilename;
     113             : }
     114             : 
     115             : /************************************************************************/
     116             : /*                            SetupParser()                             */
     117             : /************************************************************************/
     118             : 
     119           6 : bool NASReader::SetupParser()
     120             : 
     121             : {
     122           6 :     if (m_fp == nullptr)
     123           3 :         m_fp = VSIFOpenL(m_pszFilename, "rb");
     124           6 :     if (m_fp == nullptr)
     125           0 :         return false;
     126           6 :     VSIFSeekL(m_fp, 0, SEEK_SET);
     127             : 
     128           6 :     if (!m_bXercesInitialized)
     129             :     {
     130           3 :         if (!OGRInitializeXerces())
     131           0 :             return false;
     132           3 :         m_bXercesInitialized = true;
     133             :     }
     134             : 
     135             :     // Cleanup any old parser.
     136           6 :     if (m_poSAXReader != nullptr)
     137           0 :         CleanupParser();
     138             : 
     139             :     // Create and initialize parser.
     140           6 :     XMLCh *xmlUriValid = nullptr;
     141           6 :     XMLCh *xmlUriNS = nullptr;
     142             : 
     143             :     try
     144             :     {
     145           6 :         m_poSAXReader = XMLReaderFactory::createXMLReader();
     146             : 
     147           6 :         m_poNASHandler = new NASHandler(this);
     148             : 
     149           6 :         m_poSAXReader->setContentHandler(m_poNASHandler);
     150           6 :         m_poSAXReader->setErrorHandler(m_poNASHandler);
     151           6 :         m_poSAXReader->setLexicalHandler(m_poNASHandler);
     152           6 :         m_poSAXReader->setEntityResolver(m_poNASHandler);
     153           6 :         m_poSAXReader->setDTDHandler(m_poNASHandler);
     154           6 :         m_poSAXReader->setFeature(
     155           6 :             XMLUni::fgXercesDisableDefaultEntityResolution, true);
     156             : 
     157           6 :         xmlUriValid =
     158           6 :             XMLString::transcode("http://xml.org/sax/features/validation");
     159           6 :         xmlUriNS =
     160           6 :             XMLString::transcode("http://xml.org/sax/features/namespaces");
     161             : 
     162             : #if (OGR_GML_VALIDATION)
     163             :         m_poSAXReader->setFeature(xmlUriValid, true);
     164             :         m_poSAXReader->setFeature(xmlUriNS, true);
     165             : 
     166             :         m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
     167             :         m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true);
     168             : 
     169             :         // m_poSAXReader->setDoSchema(true);
     170             :         // m_poSAXReader->setValidationSchemaFullChecking(true);
     171             : #else
     172           6 :         m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
     173             : 
     174           6 :         m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
     175             : 
     176             : #endif
     177           6 :         XMLString::release(&xmlUriValid);
     178           6 :         XMLString::release(&xmlUriNS);
     179             :     }
     180           0 :     catch (...)
     181             :     {
     182           0 :         XMLString::release(&xmlUriValid);
     183           0 :         XMLString::release(&xmlUriNS);
     184             : 
     185           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     186             :                  "NAS: Exception initializing Xerces based GML reader.\n");
     187           0 :         return false;
     188             :     }
     189             : 
     190           6 :     m_bReadStarted = false;
     191             : 
     192             :     // Push an empty state.
     193           6 :     PushState(new GMLReadState());
     194             : 
     195           6 :     if (m_GMLInputSource == nullptr)
     196             :     {
     197           6 :         m_GMLInputSource = OGRCreateXercesInputSource(m_fp);
     198             :     }
     199             : 
     200           6 :     return true;
     201             : }
     202             : 
     203             : /************************************************************************/
     204             : /*                           CleanupParser()                            */
     205             : /************************************************************************/
     206             : 
     207          12 : void NASReader::CleanupParser()
     208             : 
     209             : {
     210          12 :     if (m_poSAXReader == nullptr)
     211           6 :         return;
     212             : 
     213          12 :     while (m_poState)
     214           6 :         PopState();
     215             : 
     216           6 :     delete m_poSAXReader;
     217           6 :     m_poSAXReader = nullptr;
     218             : 
     219           6 :     delete m_poNASHandler;
     220           6 :     m_poNASHandler = nullptr;
     221             : 
     222           6 :     delete m_poCompleteFeature;
     223           6 :     m_poCompleteFeature = nullptr;
     224             : 
     225           6 :     OGRDestroyXercesInputSource(m_GMLInputSource);
     226           6 :     m_GMLInputSource = nullptr;
     227             : 
     228           6 :     m_bReadStarted = false;
     229             : }
     230             : 
     231             : /************************************************************************/
     232             : /*                            NextFeature()                             */
     233             : /************************************************************************/
     234             : 
     235          11 : GMLFeature *NASReader::NextFeature()
     236             : 
     237             : {
     238          11 :     GMLFeature *poReturn = nullptr;
     239             : 
     240             :     try
     241             :     {
     242          11 :         if (!m_bReadStarted)
     243             :         {
     244           6 :             if (m_poSAXReader == nullptr)
     245           3 :                 SetupParser();
     246             : 
     247           6 :             if (m_poSAXReader == nullptr)
     248           0 :                 return nullptr;
     249             : 
     250           6 :             if (!m_poSAXReader->parseFirst(*m_GMLInputSource, m_oToFill))
     251           0 :                 return nullptr;
     252           6 :             m_bReadStarted = true;
     253             :         }
     254             : 
     255        1726 :         while (m_poCompleteFeature == nullptr && !m_bStopParsing &&
     256        1707 :                m_poSAXReader->parseNext(m_oToFill))
     257             :         {
     258             :         }
     259             : 
     260          11 :         poReturn = m_poCompleteFeature;
     261          11 :         m_poCompleteFeature = nullptr;
     262             :     }
     263           0 :     catch (const XMLException &toCatch)
     264             :     {
     265           0 :         m_bStopParsing = true;
     266           0 :         CPLDebug("NAS", "Error during NextFeature()! Message:\n%s",
     267           0 :                  transcode(toCatch.getMessage()).c_str());
     268             :     }
     269             : 
     270          11 :     return poReturn;
     271             : }
     272             : 
     273             : /************************************************************************/
     274             : /*                            PushFeature()                             */
     275             : /*                                                                      */
     276             : /*      Create a feature based on the named element.  If the            */
     277             : /*      corresponding feature class doesn't exist yet, then create      */
     278             : /*      it now.  A new GMLReadState will be created for the feature,    */
     279             : /*      and it will be placed within that state.  The state is          */
     280             : /*      pushed onto the readstate stack.                                */
     281             : /************************************************************************/
     282             : 
     283           8 : void NASReader::PushFeature(const char *pszElement, const Attributes &attrs)
     284             : 
     285             : {
     286             :     /* -------------------------------------------------------------------- */
     287             :     /*      Find the class of this element.                                 */
     288             :     /* -------------------------------------------------------------------- */
     289           8 :     int iClass = 0;
     290          10 :     for (; iClass < GetClassCount(); iClass++)
     291             :     {
     292           7 :         if (strcmp(pszElement, GetClass(iClass)->GetElementName()) == 0)
     293           5 :             break;
     294             :     }
     295             : 
     296             :     /* -------------------------------------------------------------------- */
     297             :     /*      Create a new feature class for this element, if there is no     */
     298             :     /*      existing class for it.                                          */
     299             :     /* -------------------------------------------------------------------- */
     300           8 :     if (iClass == GetClassCount())
     301             :     {
     302           3 :         CPLAssert(!IsClassListLocked());
     303             : 
     304           3 :         GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
     305             : 
     306           3 :         if (EQUAL(pszElement, "Delete"))
     307             :         {
     308             :             const struct
     309             :             {
     310             :                 const char *pszName;
     311             :                 GMLPropertyType eType;
     312             :                 int width;
     313           2 :             } types[] = {
     314             :                 {"typeName", GMLPT_String, -1},
     315             :                 {"FeatureId", GMLPT_String, -1},
     316             :                 {"context", GMLPT_String, -1},
     317             :                 {"safeToIgnore", GMLPT_String, -1},
     318             :                 {"replacedBy", GMLPT_String, -1},
     319             :                 {"anlass", GMLPT_StringList, -1},
     320             :                 {"endet", GMLPT_String, 20},
     321             :                 {"ignored", GMLPT_String, -1},
     322             :             };
     323             : 
     324          18 :             for (unsigned int i = 0; i < CPL_ARRAYSIZE(types); i++)
     325             :             {
     326             :                 GMLPropertyDefn *poPDefn =
     327          16 :                     new GMLPropertyDefn(types[i].pszName, types[i].pszName);
     328             : 
     329          16 :                 poPDefn->SetType(types[i].eType);
     330          16 :                 if (types[i].width > 0)
     331           2 :                     poPDefn->SetWidth(types[i].width);
     332             : 
     333          16 :                 poNewClass->AddProperty(poPDefn);
     334             :             }
     335             :         }
     336             : 
     337           3 :         iClass = AddClass(poNewClass);
     338             :     }
     339             : 
     340             :     /* -------------------------------------------------------------------- */
     341             :     /*      Create a feature of this feature class.                         */
     342             :     /* -------------------------------------------------------------------- */
     343           8 :     GMLFeature *poFeature = new GMLFeature(GetClass(iClass));
     344             : 
     345             :     /* -------------------------------------------------------------------- */
     346             :     /*      Create and push a new read state.                               */
     347             :     /* -------------------------------------------------------------------- */
     348           8 :     GMLReadState *poState = new GMLReadState();
     349           8 :     poState->m_poFeature = poFeature;
     350           8 :     PushState(poState);
     351             : 
     352             :     /* -------------------------------------------------------------------- */
     353             :     /*      Check for gml:id, and if found push it as an attribute named    */
     354             :     /*      gml_id.                                                         */
     355             :     /* -------------------------------------------------------------------- */
     356           8 :     const XMLCh achGmlId[] = {'g', 'm', 'l', ':', 'i', 'd', 0};
     357           8 :     int nFIDIndex = attrs.getIndex(achGmlId);
     358           8 :     if (nFIDIndex != -1)
     359             :     {
     360           2 :         char *pszFID = CPLStrdup(transcode(attrs.getValue(nFIDIndex)));
     361           2 :         SetFeaturePropertyDirectly("gml_id", pszFID);
     362             :     }
     363           8 : }
     364             : 
     365             : /************************************************************************/
     366             : /*                          IsFeatureElement()                          */
     367             : /*                                                                      */
     368             : /*      Based on context and the element name, is this element a new    */
     369             : /*      GML feature element?                                            */
     370             : /************************************************************************/
     371             : 
     372         151 : bool NASReader::IsFeatureElement(const char *pszElement)
     373             : 
     374             : {
     375         151 :     CPLAssert(m_poState != nullptr);
     376             : 
     377         151 :     const char *pszLast = m_poState->GetLastComponent();
     378         151 :     const int nLen = static_cast<int>(strlen(pszLast));
     379             : 
     380             :     // There seem to be two major NAS classes of feature identifiers
     381             :     // -- either a wfs:Insert or a gml:featureMember/wfs:member
     382             : 
     383         151 :     if ((nLen < 6 || !EQUAL(pszLast + nLen - 6, "Insert")) &&
     384         151 :         (nLen < 13 || !EQUAL(pszLast + nLen - 13, "featureMember")) &&
     385         151 :         (nLen < 6 || !EQUAL(pszLast + nLen - 6, "member")) &&
     386         141 :         (nLen < 7 || !EQUAL(pszLast + nLen - 7, "Replace")))
     387         146 :         return false;
     388             : 
     389             :     // If the class list isn't locked, any element that is a featureMember
     390             :     // will do.
     391           5 :     if (EQUAL(pszElement, "Filter"))
     392           2 :         return false;
     393             : 
     394           3 :     if (!IsClassListLocked())
     395           3 :         return true;
     396             : 
     397           0 :     if (EQUAL(pszElement, "Delete"))
     398           0 :         return false;
     399             : 
     400             :     // otherwise, find a class with the desired element name.
     401           0 :     for (int i = 0; i < GetClassCount(); i++)
     402             :     {
     403           0 :         if (EQUAL(pszElement, GetClass(i)->GetElementName()))
     404           0 :             return true;
     405             :     }
     406             : 
     407           0 :     return false;
     408             : }
     409             : 
     410             : /************************************************************************/
     411             : /*                         IsAttributeElement()                         */
     412             : /************************************************************************/
     413             : 
     414         106 : bool NASReader::IsAttributeElement(const char *pszElement,
     415             :                                    const Attributes &attrs)
     416             : 
     417             : {
     418         106 :     if (m_poState->m_poFeature == nullptr)
     419           0 :         return false;
     420             : 
     421         106 :     GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
     422             : 
     423             :     // If the schema is not yet locked, then any simple element
     424             :     // is potentially an attribute.
     425         106 :     if (!poClass->IsSchemaLocked())
     426         106 :         return true;
     427             : 
     428             :     // Otherwise build the path to this element into a single string
     429             :     // and compare against known attributes.
     430           0 :     CPLString osElemPath;
     431             : 
     432           0 :     if (m_poState->m_nPathLength == 0)
     433           0 :         osElemPath = pszElement;
     434             :     else
     435             :     {
     436           0 :         osElemPath = m_poState->osPath;
     437           0 :         osElemPath += "|";
     438           0 :         osElemPath += pszElement;
     439             :     }
     440             : 
     441           0 :     if (poClass->GetPropertyIndexBySrcElement(
     442           0 :             osElemPath.c_str(), static_cast<int>(osElemPath.size())) >= 0)
     443           0 :         return true;
     444             : 
     445           0 :     for (unsigned int idx = 0; idx < attrs.getLength(); ++idx)
     446             :     {
     447           0 :         CPLString osAttrName = transcode(attrs.getQName(idx));
     448           0 :         CPLString osAttrPath;
     449             : 
     450           0 :         const char *pszName = strchr(osAttrName.c_str(), ':');
     451           0 :         if (pszName)
     452             :         {
     453           0 :             osAttrPath = osElemPath + "@" + (pszName + 1);
     454           0 :             if (poClass->GetPropertyIndexBySrcElement(
     455           0 :                     osAttrPath.c_str(), static_cast<int>(osAttrPath.size())) >=
     456             :                 0)
     457           0 :                 return true;
     458             :         }
     459             : 
     460           0 :         osAttrPath = osElemPath + "@" + osAttrName;
     461           0 :         if (poClass->GetPropertyIndexBySrcElement(
     462           0 :                 osAttrPath.c_str(), static_cast<int>(osAttrPath.size())) >= 0)
     463           0 :             return true;
     464             :     }
     465             : 
     466           0 :     return false;
     467             : }
     468             : 
     469             : /************************************************************************/
     470             : /*                              PopState()                              */
     471             : /************************************************************************/
     472             : 
     473          14 : void NASReader::PopState()
     474             : 
     475             : {
     476          14 :     if (m_poState != nullptr)
     477             :     {
     478          14 :         if (m_poState->m_poFeature != nullptr && m_poCompleteFeature == nullptr)
     479             :         {
     480           8 :             m_poCompleteFeature = m_poState->m_poFeature;
     481           8 :             m_poState->m_poFeature = nullptr;
     482             :         }
     483           6 :         else if (m_poState->m_poFeature != nullptr)
     484             :         {
     485           0 :             delete m_poState->m_poFeature;
     486           0 :             m_poState->m_poFeature = nullptr;
     487             :         }
     488             : 
     489          14 :         GMLReadState *poParent = m_poState->m_poParentState;
     490             : 
     491          14 :         delete m_poState;
     492          14 :         m_poState = poParent;
     493             :     }
     494          14 : }
     495             : 
     496             : /************************************************************************/
     497             : /*                             PushState()                              */
     498             : /************************************************************************/
     499             : 
     500          14 : void NASReader::PushState(GMLReadState *poState)
     501             : 
     502             : {
     503          14 :     poState->m_poParentState = m_poState;
     504          14 :     m_poState = poState;
     505          14 : }
     506             : 
     507             : /************************************************************************/
     508             : /*                              GetClass()                              */
     509             : /************************************************************************/
     510             : 
     511          21 : GMLFeatureClass *NASReader::GetClass(int iClass) const
     512             : 
     513             : {
     514          21 :     if (iClass < 0 || iClass >= m_nClassCount)
     515           0 :         return nullptr;
     516             : 
     517          21 :     return m_papoClass[iClass];
     518             : }
     519             : 
     520             : /************************************************************************/
     521             : /*                              GetClass()                              */
     522             : /************************************************************************/
     523             : 
     524           6 : GMLFeatureClass *NASReader::GetClass(const char *pszName) const
     525             : 
     526             : {
     527           8 :     for (int iClass = 0; iClass < m_nClassCount; iClass++)
     528             :     {
     529           5 :         if (strcmp(m_papoClass[iClass]->GetName(), pszName) == 0)
     530           3 :             return m_papoClass[iClass];
     531             :     }
     532             : 
     533           3 :     return nullptr;
     534             : }
     535             : 
     536             : /************************************************************************/
     537             : /*                              AddClass()                              */
     538             : /************************************************************************/
     539             : 
     540           3 : int NASReader::AddClass(GMLFeatureClass *poNewClass)
     541             : 
     542             : {
     543           3 :     CPLAssert(poNewClass != nullptr &&
     544             :               GetClass(poNewClass->GetName()) == nullptr);
     545             : 
     546           3 :     m_nClassCount++;
     547           3 :     m_papoClass = static_cast<GMLFeatureClass **>(
     548           3 :         CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
     549             : 
     550             :     // keep delete the last entry
     551           4 :     if (m_nClassCount > 1 &&
     552           1 :         EQUAL(m_papoClass[m_nClassCount - 2]->GetName(), "Delete"))
     553             :     {
     554           0 :         m_papoClass[m_nClassCount - 1] = m_papoClass[m_nClassCount - 2];
     555           0 :         m_papoClass[m_nClassCount - 2] = poNewClass;
     556           0 :         return m_nClassCount - 2;
     557             :     }
     558             :     else
     559             :     {
     560           3 :         m_papoClass[m_nClassCount - 1] = poNewClass;
     561           3 :         return m_nClassCount - 1;
     562             :     }
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                            ClearClasses()                            */
     567             : /************************************************************************/
     568             : 
     569           3 : void NASReader::ClearClasses()
     570             : 
     571             : {
     572           3 :     CPLDebug("NAS", "Clearing classes.");
     573             : 
     574           6 :     for (int i = 0; i < m_nClassCount; i++)
     575           3 :         delete m_papoClass[i];
     576           3 :     CPLFree(m_papoClass);
     577             : 
     578           3 :     m_nClassCount = 0;
     579           3 :     m_papoClass = nullptr;
     580           3 : }
     581             : 
     582             : /************************************************************************/
     583             : /*                     SetFeaturePropertyDirectly()                     */
     584             : /*                                                                      */
     585             : /*      Set the property value on the current feature, adding the       */
     586             : /*      property name to the GMLFeatureClass if required.               */
     587             : /*      The pszValue ownership is passed to this function.              */
     588             : /************************************************************************/
     589             : 
     590          82 : void NASReader::SetFeaturePropertyDirectly(const char *pszElement,
     591             :                                            char *pszValue)
     592             : 
     593             : {
     594          82 :     GMLFeature *poFeature = GetState()->m_poFeature;
     595             : 
     596          82 :     CPLAssert(poFeature != nullptr);
     597             : 
     598             :     /* -------------------------------------------------------------------- */
     599             :     /*      Does this property exist in the feature class?  If not, add     */
     600             :     /*      it.                                                             */
     601             :     /* -------------------------------------------------------------------- */
     602          82 :     GMLFeatureClass *poClass = poFeature->GetClass();
     603         164 :     int iProperty = poClass->GetPropertyIndexBySrcElement(
     604          82 :         pszElement, static_cast<int>(strlen(pszElement)));
     605             : 
     606          82 :     if (iProperty < 0)
     607             :     {
     608          26 :         if (poClass->IsSchemaLocked())
     609             :         {
     610             :             // CPLDebug("NAS", "Encountered property %s missing from class %s schema [%s].", pszElement, poClass->GetName(), pszValue);
     611           0 :             CPLFree(pszValue);
     612           0 :             return;
     613             :         }
     614             : 
     615          26 :         iProperty = poClass->GetPropertyCount();
     616             : 
     617          52 :         CPLString osFieldName;
     618             : 
     619          26 :         if (strchr(pszElement, '|') == nullptr)
     620          11 :             osFieldName = pszElement;
     621             :         else
     622             :         {
     623          15 :             osFieldName = strrchr(pszElement, '|') + 1;
     624          15 :             if (poClass->GetPropertyIndex(osFieldName) != -1)
     625           2 :                 osFieldName = pszElement;
     626             :         }
     627             : 
     628             :         // Does this conflict with an existing property name?
     629          26 :         while (poClass->GetProperty(osFieldName) != nullptr)
     630             :         {
     631           0 :             osFieldName += "_";
     632             :         }
     633             : 
     634          26 :         GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName, pszElement);
     635             : 
     636          26 :         if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""), "ALWAYS_STRING"))
     637           0 :             poPDefn->SetType(GMLPT_String);
     638             : 
     639          26 :         poClass->AddProperty(poPDefn);
     640             :     }
     641             : 
     642          82 :     if (GMLPropertyDefn::IsSimpleType(
     643             :             poClass->GetProperty(iProperty)->GetType()))
     644             :     {
     645          44 :         const GMLProperty *poProp = poFeature->GetProperty(iProperty);
     646          44 :         if (poProp && poProp->nSubProperties > 0)
     647             :         {
     648           2 :             int iId = poClass->GetPropertyIndex("gml_id");
     649           2 :             const GMLProperty *poIdProp = poFeature->GetProperty(iId);
     650             : 
     651           4 :             CPLError(CE_Warning, CPLE_AppDefined,
     652             :                      "NAS: Overwriting existing property %s.%s of value '%s' "
     653             :                      "with '%s' (gml_id: %s; type:%d).",
     654             :                      poClass->GetName(), pszElement,
     655           2 :                      poProp->papszSubProperties[0], pszValue,
     656           2 :                      poIdProp && poIdProp->nSubProperties > 0 &&
     657           2 :                              poIdProp->papszSubProperties &&
     658           2 :                              poIdProp->papszSubProperties[0]
     659           2 :                          ? poIdProp->papszSubProperties[0]
     660             :                          : "(null)",
     661           2 :                      poClass->GetProperty(iProperty)->GetType());
     662             :         }
     663             :     }
     664             : 
     665             :     /* -------------------------------------------------------------------- */
     666             :     /*      Set the property                                                */
     667             :     /* -------------------------------------------------------------------- */
     668          82 :     poFeature->SetPropertyDirectly(iProperty, pszValue);
     669             : 
     670             :     /* -------------------------------------------------------------------- */
     671             :     /*      Do we need to update the property type?                         */
     672             :     /* -------------------------------------------------------------------- */
     673          82 :     if (!poClass->IsSchemaLocked())
     674             :     {
     675          82 :         auto poClassProperty = poClass->GetProperty(iProperty);
     676          82 :         if (poClassProperty)
     677             :         {
     678             :             // coverity[dereference]
     679          82 :             poClassProperty->AnalysePropertyValue(
     680             :                 poFeature->GetProperty(iProperty));
     681             :         }
     682             :         else
     683             :         {
     684           0 :             CPLAssert(false);
     685             :         }
     686             :     }
     687             : }
     688             : 
     689             : /************************************************************************/
     690             : /*                            LoadClasses()                             */
     691             : /************************************************************************/
     692             : 
     693           0 : bool NASReader::LoadClasses(const char *pszFile)
     694             : 
     695             : {
     696             :     // Add logic later to determine reasonable default schema file.
     697           0 :     if (pszFile == nullptr)
     698           0 :         return false;
     699             : 
     700           0 :     CPLDebug("NAS", "Loading classes from %s", pszFile);
     701             : 
     702             :     /* -------------------------------------------------------------------- */
     703             :     /*      Load the raw XML file.                                          */
     704             :     /* -------------------------------------------------------------------- */
     705           0 :     VSILFILE *fp = VSIFOpenL(pszFile, "rb");
     706             : 
     707           0 :     if (fp == nullptr)
     708             :     {
     709           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "NAS: Failed to open file %s.",
     710             :                  pszFile);
     711           0 :         return false;
     712             :     }
     713             : 
     714           0 :     VSIFSeekL(fp, 0, SEEK_END);
     715           0 :     int nLength = static_cast<int>(VSIFTellL(fp));
     716           0 :     VSIFSeekL(fp, 0, SEEK_SET);
     717             : 
     718           0 :     char *pszWholeText = static_cast<char *>(VSIMalloc(nLength + 1));
     719           0 :     if (pszWholeText == nullptr)
     720             :     {
     721           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     722             :                  "NAS: Failed to allocate %d byte buffer for %s,\n"
     723             :                  "is this really a GMLFeatureClassList file?",
     724             :                  nLength, pszFile);
     725           0 :         VSIFCloseL(fp);
     726           0 :         return false;
     727             :     }
     728             : 
     729           0 :     if (VSIFReadL(pszWholeText, nLength, 1, fp) != 1)
     730             :     {
     731           0 :         VSIFree(pszWholeText);
     732           0 :         VSIFCloseL(fp);
     733           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NAS: Read failed on %s.",
     734             :                  pszFile);
     735           0 :         return false;
     736             :     }
     737           0 :     pszWholeText[nLength] = '\0';
     738             : 
     739           0 :     VSIFCloseL(fp);
     740             : 
     741           0 :     if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
     742             :     {
     743           0 :         VSIFree(pszWholeText);
     744           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     745             :                  "NAS: File %s does not contain a GMLFeatureClassList tree.",
     746             :                  pszFile);
     747           0 :         return false;
     748             :     }
     749             : 
     750             :     /* -------------------------------------------------------------------- */
     751             :     /*      Convert to XML parse tree.                                      */
     752             :     /* -------------------------------------------------------------------- */
     753           0 :     CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
     754           0 :     VSIFree(pszWholeText);
     755             : 
     756             :     // We assume parser will report errors via CPL.
     757           0 :     if (psRoot.get() == nullptr)
     758           0 :         return false;
     759             : 
     760           0 :     if (psRoot->eType != CXT_Element ||
     761           0 :         !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
     762             :     {
     763           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     764             :                  "NAS: File %s is not a GMLFeatureClassList document.",
     765             :                  pszFile);
     766           0 :         return false;
     767             :     }
     768             : 
     769             :     /* -------------------------------------------------------------------- */
     770             :     /*      Extract feature classes for all definitions found.              */
     771             :     /* -------------------------------------------------------------------- */
     772           0 :     for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
     773           0 :          psThis = psThis->psNext)
     774             :     {
     775           0 :         if (psThis->eType == CXT_Element &&
     776           0 :             EQUAL(psThis->pszValue, "GMLFeatureClass"))
     777             :         {
     778           0 :             GMLFeatureClass *poClass = new GMLFeatureClass();
     779             : 
     780           0 :             if (!poClass->InitializeFromXML(psThis))
     781             :             {
     782           0 :                 delete poClass;
     783           0 :                 return false;
     784             :             }
     785             : 
     786           0 :             poClass->SetSchemaLocked(true);
     787             : 
     788           0 :             AddClass(poClass);
     789             :         }
     790             :     }
     791             : 
     792           0 :     SetClassListLocked(true);
     793             : 
     794           0 :     return true;
     795             : }
     796             : 
     797             : /************************************************************************/
     798             : /*                            SaveClasses()                             */
     799             : /************************************************************************/
     800             : 
     801           2 : bool NASReader::SaveClasses(const char *pszFile)
     802             : 
     803             : {
     804             :     // Add logic later to determine reasonable default schema file.
     805           2 :     if (pszFile == nullptr)
     806           0 :         return false;
     807             : 
     808             :     /* -------------------------------------------------------------------- */
     809             :     /*      Create in memory schema tree.                                   */
     810             :     /* -------------------------------------------------------------------- */
     811             :     CPLXMLNode *psRoot =
     812           2 :         CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
     813             : 
     814           5 :     for (int iClass = 0; iClass < GetClassCount(); iClass++)
     815             :     {
     816           3 :         GMLFeatureClass *poClass = GetClass(iClass);
     817             : 
     818           3 :         CPLAddXMLChild(psRoot, poClass->SerializeToXML());
     819             :     }
     820             : 
     821             :     /* -------------------------------------------------------------------- */
     822             :     /*      Serialize to disk.                                              */
     823             :     /* -------------------------------------------------------------------- */
     824           2 :     char *pszWholeText = CPLSerializeXMLTree(psRoot);
     825             : 
     826           2 :     CPLDestroyXMLNode(psRoot);
     827             : 
     828           2 :     VSILFILE *fp = VSIFOpenL(pszFile, "wb");
     829             : 
     830           2 :     bool bSuccess = true;
     831           2 :     if (fp == nullptr)
     832           0 :         bSuccess = false;
     833           2 :     else if (VSIFWriteL(pszWholeText, strlen(pszWholeText), 1, fp) != 1)
     834             :     {
     835           0 :         VSIFCloseL(fp);
     836           0 :         bSuccess = false;
     837             :     }
     838             :     else
     839             :     {
     840           2 :         if (VSIFWriteL(pszWholeText, strlen(pszWholeText), 1, fp) != 1)
     841           0 :             bSuccess = false;
     842           2 :         VSIFCloseL(fp);
     843             :     }
     844             : 
     845           2 :     CPLFree(pszWholeText);
     846             : 
     847           2 :     return bSuccess;
     848             : }
     849             : 
     850             : /************************************************************************/
     851             : /*                          PrescanForSchema()                          */
     852             : /*                                                                      */
     853             : /*      For now we use a pretty dumb approach of just doing a normal    */
     854             : /*      scan of the whole file, building up the schema information.     */
     855             : /*      Eventually we hope to do a more efficient scan when just        */
     856             : /*      looking for schema information.                                 */
     857             : /************************************************************************/
     858             : 
     859           3 : bool NASReader::PrescanForSchema(bool bGetExtents, bool /*bOnlyDetectSRS*/)
     860             : {
     861           3 :     if (m_pszFilename == nullptr)
     862           0 :         return false;
     863             : 
     864           3 :     CPLDebug("NAS", "Prescanning %s.", m_pszFilename);
     865             : 
     866           3 :     SetClassListLocked(false);
     867             : 
     868           3 :     if (!SetupParser())
     869           0 :         return false;
     870             : 
     871           3 :     std::string osWork;
     872             : 
     873           3 :     GMLFeature *poFeature = nullptr;
     874           8 :     while ((poFeature = NextFeature()) != nullptr)
     875             :     {
     876           5 :         GMLFeatureClass *poClass = poFeature->GetClass();
     877             : 
     878           5 :         if (poClass->GetFeatureCount() == -1)
     879           3 :             poClass->SetFeatureCount(1);
     880             :         else
     881           2 :             poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
     882             : 
     883           5 :         if (bGetExtents)
     884             :         {
     885           5 :             OGRGeometry *poGeometry = nullptr;
     886             : 
     887             :             const CPLXMLNode *const *papsGeometry =
     888           5 :                 poFeature->GetGeometryList();
     889           5 :             if (papsGeometry[0] != nullptr)
     890             :             {
     891             :                 poGeometry =
     892           1 :                     (OGRGeometry *)OGR_G_CreateFromGMLTree(papsGeometry[0]);
     893           1 :                 poGeometry = ConvertGeometry(poGeometry);
     894             :             }
     895             : 
     896           5 :             if (poGeometry != nullptr)
     897             :             {
     898           1 :                 OGREnvelope sEnvelope;
     899             : 
     900           1 :                 if (poClass->GetGeometryPropertyCount() == 0)
     901           2 :                     poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
     902           1 :                         "", "", wkbUnknown, -1, true));
     903             : 
     904             :                 OGRwkbGeometryType eGType =
     905             :                     (OGRwkbGeometryType)poClass->GetGeometryProperty(0)
     906           1 :                         ->GetType();
     907             : 
     908             :                 // Merge SRSName into layer.
     909             :                 const char *pszSRSName =
     910           1 :                     GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, false);
     911             :                 // if (pszSRSName != NULL)
     912             :                 //     m_bCanUseGlobalSRSName = FALSE;
     913           1 :                 poClass->MergeSRSName(pszSRSName);
     914             : 
     915             :                 // Merge geometry type into layer.
     916           1 :                 if (poClass->GetFeatureCount() == 1 && eGType == wkbUnknown)
     917           1 :                     eGType = wkbNone;
     918             : 
     919           2 :                 poClass->GetGeometryProperty(0)->SetType(
     920             :                     OGRMergeGeometryTypesEx(
     921           1 :                         eGType, poGeometry->getGeometryType(), TRUE));
     922             : 
     923             :                 // merge extents.
     924           1 :                 poGeometry->getEnvelope(&sEnvelope);
     925           1 :                 delete poGeometry;
     926           1 :                 double dfXMin = 0.0;
     927           1 :                 double dfXMax = 0.0;
     928           1 :                 double dfYMin = 0.0;
     929           1 :                 double dfYMax = 0.0;
     930           1 :                 if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
     931             :                 {
     932           0 :                     dfXMin = std::min(dfXMin, sEnvelope.MinX);
     933           0 :                     dfXMax = std::max(dfXMax, sEnvelope.MaxX);
     934           0 :                     dfYMin = std::min(dfYMin, sEnvelope.MinY);
     935           0 :                     dfYMax = std::max(dfYMax, sEnvelope.MaxY);
     936             :                 }
     937             :                 else
     938             :                 {
     939           1 :                     dfXMin = sEnvelope.MinX;
     940           1 :                     dfXMax = sEnvelope.MaxX;
     941           1 :                     dfYMin = sEnvelope.MinY;
     942           1 :                     dfYMax = sEnvelope.MaxY;
     943             :                 }
     944             : 
     945           1 :                 poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
     946             :             }
     947             :             else
     948             :             {
     949           4 :                 if (poClass->GetGeometryPropertyCount() == 1 &&
     950           0 :                     poClass->GetGeometryProperty(0)->GetType() ==
     951           4 :                         (int)wkbUnknown &&
     952           0 :                     poClass->GetFeatureCount() == 1)
     953             :                 {
     954           0 :                     poClass->ClearGeometryProperties();
     955             :                 }
     956             :             }
     957             :         }
     958             : 
     959           5 :         delete poFeature;
     960             :     }
     961             : 
     962           3 :     CleanupParser();
     963             : 
     964             :     // Skip empty classes
     965           3 :     int j = 0;
     966           6 :     for (int i = 0, n = m_nClassCount; i < n; i++)
     967             :     {
     968           3 :         if (m_papoClass[i]->GetFeatureCount() > 0)
     969             :         {
     970           3 :             m_papoClass[j++] = m_papoClass[i];
     971           3 :             continue;
     972             :         }
     973             : 
     974           0 :         CPLDebug("NAS", "Skipping empty layer %s.", m_papoClass[i]->GetName());
     975             : 
     976           0 :         delete m_papoClass[i];
     977           0 :         m_papoClass[i] = nullptr;
     978             :     }
     979             : 
     980           3 :     m_nClassCount = j;
     981             : 
     982           3 :     CPLDebug("NAS", "%d remaining classes after prescan.", m_nClassCount);
     983             : 
     984           6 :     for (int i = 0; i < m_nClassCount; i++)
     985             :     {
     986           3 :         CPLDebug("NAS", "%s: " CPL_FRMT_GIB " features.",
     987           3 :                  m_papoClass[i]->GetName(), m_papoClass[i]->GetFeatureCount());
     988             :     }
     989             : 
     990           3 :     return GetClassCount() > 0;
     991             : }
     992             : 
     993             : /************************************************************************/
     994             : /*                            ResetReading()                            */
     995             : /************************************************************************/
     996             : 
     997           6 : void NASReader::ResetReading()
     998             : 
     999             : {
    1000           6 :     CleanupParser();
    1001           6 :     SetFilteredClassName(nullptr);
    1002           6 : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                       GetAttributeElementIndex()                     */
    1006             : /************************************************************************/
    1007             : 
    1008          16 : int NASReader::GetAttributeElementIndex(const char *pszElement, int nLen,
    1009             :                                         const char *pszAttrKey)
    1010             : 
    1011             : {
    1012          16 :     GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
    1013             : 
    1014             :     // Otherwise build the path to this element into a single string
    1015             :     // and compare against known attributes.
    1016          16 :     if (m_poState->m_nPathLength == 0)
    1017             :     {
    1018          16 :         if (pszAttrKey == nullptr)
    1019           0 :             return poClass->GetPropertyIndexBySrcElement(pszElement, nLen);
    1020             :         else
    1021             :         {
    1022          32 :             CPLString osElemPath;
    1023          16 :             int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey));
    1024          16 :             osElemPath.reserve(nFullLen);
    1025          16 :             osElemPath.assign(pszElement, nLen);
    1026          16 :             osElemPath.append(1, '@');
    1027          16 :             osElemPath.append(pszAttrKey);
    1028          16 :             return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
    1029          16 :                                                          nFullLen);
    1030             :         }
    1031             :     }
    1032             :     else
    1033             :     {
    1034           0 :         int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
    1035           0 :         if (pszAttrKey != nullptr)
    1036           0 :             nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
    1037             : 
    1038           0 :         CPLString osElemPath;
    1039           0 :         osElemPath.reserve(nFullLen);
    1040           0 :         osElemPath.assign(m_poState->osPath);
    1041           0 :         osElemPath.append(1, '|');
    1042           0 :         osElemPath.append(pszElement, nLen);
    1043           0 :         if (pszAttrKey != nullptr)
    1044             :         {
    1045           0 :             osElemPath.append(1, '@');
    1046           0 :             osElemPath.append(pszAttrKey);
    1047             :         }
    1048           0 :         return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
    1049           0 :                                                      nFullLen);
    1050             :     }
    1051             : 
    1052             :     return -1;
    1053             : }
    1054             : 
    1055             : /************************************************************************/
    1056             : /*                         DealWithAttributes()                         */
    1057             : /************************************************************************/
    1058             : 
    1059         106 : void NASReader::DealWithAttributes(const char *pszName, int nLenName,
    1060             :                                    const Attributes &attrs)
    1061             : 
    1062             : {
    1063         106 :     GMLFeature *poFeature = GetState()->m_poFeature;
    1064         106 :     CPLAssert(poFeature != nullptr);
    1065             : 
    1066         116 :     for (unsigned int idx = 0; idx < attrs.getLength(); ++idx)
    1067             :     {
    1068          20 :         CPLString osAttrKey = transcode(attrs.getQName(idx));
    1069          20 :         CPLString osAttrVal = transcode(attrs.getValue(idx));
    1070             : 
    1071          10 :         int nAttrIndex = 0;
    1072          10 :         const char *pszAttrKeyNoNS = strchr(osAttrKey, ':');
    1073          10 :         if (pszAttrKeyNoNS)
    1074           6 :             pszAttrKeyNoNS++;
    1075             : 
    1076          16 :         if ((pszAttrKeyNoNS &&
    1077           6 :              (nAttrIndex = GetAttributeElementIndex(pszName, nLenName,
    1078          20 :                                                     pszAttrKeyNoNS)) != -1) ||
    1079          10 :             ((nAttrIndex = GetAttributeElementIndex(pszName, nLenName,
    1080             :                                                     osAttrKey)) != -1))
    1081             :         {
    1082           0 :             const char *pszAttrVal = osAttrVal;
    1083           0 :             if (osAttrKey == "xlink:href" ||
    1084           0 :                 (pszAttrKeyNoNS && EQUAL(pszAttrKeyNoNS, "href")))
    1085             :             {
    1086           0 :                 if (STARTS_WITH_CI(pszAttrVal, "urn:adv:oid:"))
    1087           0 :                     pszAttrVal += 12;
    1088           0 :                 else if (STARTS_WITH_CI(
    1089             :                              pszAttrVal,
    1090             :                              "https://registry.gdi-de.org/codelist/"))
    1091           0 :                     pszAttrVal = strrchr(pszAttrVal, '/') + 1;
    1092             :             }
    1093             : 
    1094           0 :             poFeature->SetPropertyDirectly(nAttrIndex, CPLStrdup(pszAttrVal));
    1095           0 :             pszAttrVal = nullptr;
    1096             :         }
    1097             :     }
    1098         106 : }
    1099             : 
    1100             : /************************************************************************/
    1101             : /*                         HugeFileResolver()                           */
    1102             : /*      Returns true for success                                        */
    1103             : /************************************************************************/
    1104             : 
    1105           0 : bool NASReader::HugeFileResolver(const char * /*pszFile */,
    1106             :                                  bool /* bSqliteIsTempFile */,
    1107             :                                  int /* iSqliteCacheMB */)
    1108             : {
    1109           0 :     CPLDebug("NAS", "HugeFileResolver() not currently implemented for NAS.");
    1110           0 :     return false;
    1111             : }
    1112             : 
    1113             : /************************************************************************/
    1114             : /*                         PrescanForTemplate()                         */
    1115             : /*      Returns true for success                                        */
    1116             : /************************************************************************/
    1117             : 
    1118           0 : bool NASReader::PrescanForTemplate(void)
    1119             : 
    1120             : {
    1121           0 :     CPLDebug("NAS", "PrescanForTemplate() not currently implemented for NAS.");
    1122           0 :     return false;
    1123             : }
    1124             : 
    1125             : /************************************************************************/
    1126             : /*                           ResolveXlinks()                            */
    1127             : /*      Returns true for success                                        */
    1128             : /************************************************************************/
    1129             : 
    1130           0 : bool NASReader::ResolveXlinks(const char * /*pszFile */,
    1131             :                               bool * /*pbOutIsTempFile */,
    1132             :                               char ** /*papszSkip */, const bool /*bStrict */)
    1133             : {
    1134           0 :     CPLDebug("NAS", "ResolveXlinks() not currently implemented for NAS.");
    1135           0 :     return false;
    1136             : }
    1137             : 
    1138             : /************************************************************************/
    1139             : /*                       SetFilteredClassName()                         */
    1140             : /************************************************************************/
    1141             : 
    1142          12 : bool NASReader::SetFilteredClassName(const char *pszClassName)
    1143             : {
    1144          12 :     CPLFree(m_pszFilteredClassName);
    1145          12 :     m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
    1146          12 :     return true;
    1147             : }
    1148             : 
    1149             : /************************************************************************/
    1150             : /*                         ConvertGeometry()                            */
    1151             : /************************************************************************/
    1152             : 
    1153           2 : OGRGeometry *NASReader::ConvertGeometry(OGRGeometry *poGeom)
    1154             : {
    1155             :     // poGeom = OGRGeometryFactory::forceToLineString( poGeom, false );
    1156           2 :     if (poGeom != nullptr)
    1157             :     {
    1158           2 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1159             :         {
    1160           0 :             poGeom = OGRGeometryFactory::forceTo(poGeom, wkbLineString);
    1161             :         }
    1162             :     }
    1163           2 :     return poGeom;
    1164             : }

Generated by: LCOV version 1.14