LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ili - imdreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 344 386 89.1 %
Date: 2025-12-05 02:43:06 Functions: 21 22 95.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Interlis 1/2 Translator
       4             :  * Purpose:  IlisMeta model reader.
       5             :  * Author:   Pirmin Kalberer, Sourcepole AG
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Pirmin Kalberer, Sourcepole AG
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : // IlisMeta model: http://www.interlis.ch/models/core/IlisMeta07-20111222.ili
      14             : 
      15             : #include "cpl_minixml.h"
      16             : #include "imdreader.h"
      17             : 
      18             : #include <set>
      19             : #include <vector>
      20             : #include <algorithm>
      21             : 
      22             : typedef std::map<CPLString, CPLXMLNode *> StrNodeMap;
      23             : typedef std::vector<CPLXMLNode *> NodeVector;
      24             : typedef std::map<const CPLXMLNode *, int> NodeCountMap;
      25             : class IliClass;
      26             : // All classes with XML node for lookup.
      27             : typedef std::map<const CPLXMLNode *, IliClass *> ClassesMap;
      28             : 
      29             : /* Helper class for collection class infos */
      30             : class IliClass
      31             : {
      32             :   public:
      33             :     CPLXMLNode *node;
      34             :     int iliVersion;
      35             :     CPLString modelVersion;
      36             :     OGRFeatureDefn *poTableDefn;
      37             :     StrNodeMap &oTidLookup;
      38             :     ClassesMap &oClasses;
      39             :     NodeCountMap &oAxisCount;
      40             :     GeomFieldInfos poGeomFieldInfos;
      41             :     StructFieldInfos poStructFieldInfos;
      42             :     NodeVector oFields;
      43             :     bool isAssocClass;
      44             :     bool hasDerivedClasses;
      45             : 
      46         521 :     IliClass(CPLXMLNode *node_, int iliVersion_, const CPLString &modelVersion_,
      47             :              StrNodeMap &oTidLookup_, ClassesMap &oClasses_,
      48             :              NodeCountMap &oAxisCount_)
      49         521 :         : node(node_), iliVersion(iliVersion_), modelVersion(modelVersion_),
      50             :           oTidLookup(oTidLookup_), oClasses(oClasses_), oAxisCount(oAxisCount_),
      51             :           poGeomFieldInfos(), poStructFieldInfos(), oFields(),
      52         521 :           isAssocClass(false), hasDerivedClasses(false)
      53             :     {
      54         521 :         char *layerName = LayerName();
      55         521 :         poTableDefn = new OGRFeatureDefn(layerName);
      56         521 :         poTableDefn->Reference();
      57         521 :         CPLFree(layerName);
      58         521 :     }
      59             : 
      60         521 :     ~IliClass()
      61         521 :     {
      62         521 :         poTableDefn->Release();
      63         521 :     }
      64             : 
      65        1171 :     const char *GetName() const
      66             :     {
      67        1171 :         return poTableDefn->GetName();
      68             :     }
      69             : 
      70        1042 :     const char *GetIliName()
      71             :     {
      72        1042 :         return CPLGetXMLValue(node, "TID",
      73        2084 :                               CPLGetXMLValue(node, "ili:tid", nullptr));
      74             :     }
      75             : 
      76         521 :     char *LayerName()
      77             :     {
      78         521 :         const char *psClassTID = GetIliName();
      79         521 :         if (iliVersion == 1)
      80             :         {
      81             :             // Skip topic and replace . with __
      82             :             char **papszTokens =
      83         323 :                 CSLTokenizeString2(psClassTID, ".", CSLT_ALLOWEMPTYTOKENS);
      84             : 
      85         646 :             CPLString layername;
      86         709 :             for (int i = 1; papszTokens != nullptr && papszTokens[i] != nullptr;
      87             :                  i++)
      88             :             {
      89         386 :                 if (i > 1)
      90          63 :                     layername += "__";
      91         386 :                 layername += papszTokens[i];
      92             :             }
      93         323 :             CSLDestroy(papszTokens);
      94         323 :             return CPLStrdup(layername);
      95             :         }
      96         198 :         else if (EQUAL(modelVersion, "2.4"))
      97             :         {
      98             :             // Remove namespace
      99             :             const CPLStringList aosTokens(
     100         172 :                 CSLTokenizeString2(psClassTID, ".", 0));
     101          86 :             return CPLStrdup(aosTokens[aosTokens.size() - 1]);
     102             :         }
     103             :         else
     104             :         {
     105         112 :             return CPLStrdup(psClassTID);
     106             :         }
     107             :     }
     108             : 
     109        1199 :     void AddFieldNode(CPLXMLNode *nodeIn, int iOrderPos)
     110             :     {
     111        1199 :         if (iOrderPos < 0 || iOrderPos > 100000)
     112             :         {
     113           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid order pos = %d",
     114             :                      iOrderPos);
     115           0 :             return;
     116             :         }
     117        1199 :         if (iOrderPos >= (int)oFields.size())
     118        1021 :             oFields.resize(iOrderPos + 1);
     119             : #ifdef DEBUG_VERBOSE
     120             :         CPLDebug("OGR_ILI", "Register field with OrderPos %d to Class %s",
     121             :                  iOrderPos, GetName());
     122             : #endif
     123        1199 :         oFields[iOrderPos] = nodeIn;
     124             :     }
     125             : 
     126          86 :     void AddRoleNode(CPLXMLNode *nodeIn, int iOrderPos)
     127             :     {
     128          86 :         isAssocClass = true;
     129          86 :         AddFieldNode(nodeIn, iOrderPos);
     130          86 :     }
     131             : 
     132         354 :     bool isEmbedded()
     133             :     {
     134         354 :         if (isAssocClass)
     135          89 :             for (NodeVector::const_iterator it = oFields.begin();
     136         138 :                  it != oFields.end(); ++it)
     137             :             {
     138          77 :                 if (*it == nullptr)
     139           0 :                     continue;
     140         154 :                 if (CPLTestBool(CPLGetXMLValue(
     141          77 :                         *it, "EmbeddedTransfer",
     142          77 :                         CPLGetXMLValue(*it, "IlisMeta16:EmbeddedTransfer",
     143             :                                        "FALSE"))))
     144          28 :                     return true;
     145             :             }
     146         326 :         return false;
     147             :     }
     148             : 
     149             :     // Add additional Geometry table for Interlis 1
     150          14 :     void AddGeomTable(const CPLString &layerName, const char *psFieldName,
     151             :                       OGRwkbGeometryType eType, bool bRefTIDField = false)
     152             :     {
     153          14 :         OGRFeatureDefn *poGeomTableDefn = new OGRFeatureDefn(layerName);
     154          28 :         OGRFieldDefn fieldDef("_TID", OFTString);
     155          14 :         poGeomTableDefn->AddFieldDefn(&fieldDef);
     156          14 :         if (bRefTIDField)
     157             :         {
     158          18 :             OGRFieldDefn fieldDefRef("_RefTID", OFTString);
     159           9 :             poGeomTableDefn->AddFieldDefn(&fieldDefRef);
     160             :         }
     161          14 :         poGeomTableDefn->DeleteGeomFieldDefn(0);
     162          14 :         OGRGeomFieldDefn fieldDefGeom(psFieldName, eType);
     163          14 :         poGeomTableDefn->AddGeomFieldDefn(&fieldDefGeom);
     164          14 :         CPLDebug("OGR_ILI", "Adding geometry table %s for field %s",
     165          14 :                  poGeomTableDefn->GetName(), psFieldName);
     166          14 :         poGeomFieldInfos[psFieldName].SetGeomTableDefn(poGeomTableDefn);
     167          14 :     }
     168             : 
     169         930 :     void AddField(const char *psName, OGRFieldType fieldType) const
     170             :     {
     171        1860 :         OGRFieldDefn fieldDef(psName, fieldType);
     172         930 :         poTableDefn->AddFieldDefn(&fieldDef);
     173         930 :         CPLDebug("OGR_ILI", "Adding field '%s' to Class %s", psName, GetName());
     174         930 :     }
     175             : 
     176         123 :     void AddGeomField(const char *psName, OGRwkbGeometryType geomType) const
     177             :     {
     178         246 :         OGRGeomFieldDefn fieldDef(psName, geomType);
     179             :         // oGFld.SetSpatialRef(geomlayer->GetSpatialRef());
     180         123 :         poTableDefn->AddGeomFieldDefn(&fieldDef);
     181         123 :         CPLDebug("OGR_ILI", "Adding geometry field '%s' to Class %s", psName,
     182             :                  GetName());
     183         123 :     }
     184             : 
     185          82 :     void AddCoord(const char *psName, const CPLXMLNode *psTypeNode) const
     186             :     {
     187          82 :         auto oIter = oAxisCount.find(psTypeNode);
     188          82 :         int dim = (oIter == oAxisCount.end()) ? 0 : oIter->second;
     189          82 :         if (dim == 0)
     190           5 :             dim = 2;  // Area center points have no Axis spec
     191          82 :         if (iliVersion == 1)
     192             :         {
     193         196 :             for (int i = 0; i < dim; i++)
     194             :             {
     195         131 :                 AddField(CPLSPrintf("%s_%d", psName, i), OFTReal);
     196             :             }
     197             :         }
     198          82 :         OGRwkbGeometryType geomType = (dim > 2) ? wkbPoint25D : wkbPoint;
     199          82 :         AddGeomField(psName, geomType);
     200          82 :     }
     201             : 
     202          36 :     OGRFieldType GetFormattedType(CPLXMLNode *nodeIn)
     203             :     {
     204          36 :         const char *psRefSuper = CPLGetXMLValue(
     205             :             nodeIn, "Super.REF",
     206             :             CPLGetXMLValue(nodeIn, "IlisMeta16:Super.ili:ref", nullptr));
     207          36 :         if (psRefSuper)
     208           0 :             return GetFormattedType(oTidLookup[psRefSuper]);
     209             : 
     210          36 :         return OFTString;  // TODO: Time, Date, etc. if possible
     211             :     }
     212             : 
     213         521 :     void InitFieldDefinitions()
     214             :     {
     215             :         // Delete default geometry field
     216         521 :         poTableDefn->DeleteGeomFieldDefn(0);
     217             : 
     218        1563 :         const char *psKind = CPLGetXMLValue(
     219         521 :             node, "Kind", CPLGetXMLValue(node, "IlisMeta16:Kind", ""));
     220             : #ifdef DEBUG_VERBOSE
     221             :         CPLDebug("OGR_ILI", "InitFieldDefinitions of '%s' kind: %s", GetName(),
     222             :                  psKind);
     223             : #endif
     224         521 :         if (EQUAL(psKind, "Structure"))
     225             :         {
     226             :             // add foreign_key field
     227         506 :             OGRFieldDefn ofieldDefn1("REF_NAME", OFTString);
     228         253 :             poTableDefn->AddFieldDefn(&ofieldDefn1);
     229         506 :             OGRFieldDefn ofieldDefn2("REF_ID", OFTString);
     230         253 :             poTableDefn->AddFieldDefn(&ofieldDefn2);
     231             :         }
     232             :         else
     233             :         {  // Class
     234             :             // add TID field
     235         268 :             const char *psTidColName = (iliVersion == 1) ? "_TID" : "TID";
     236         536 :             OGRFieldDefn ofieldDefn(psTidColName, OFTString);
     237         268 :             poTableDefn->AddFieldDefn(&ofieldDefn);
     238             :         }
     239         521 :         if (CPLTestBool(CPLGetXMLValue(node, "Abstract", "FALSE")))
     240         121 :             hasDerivedClasses = true;
     241         521 :     }
     242             : 
     243         884 :     const CPLXMLNode *TidLookup(const char *pszKey) const
     244             :     {
     245         884 :         if (pszKey == nullptr)
     246             :         {
     247           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     248             :                      "Null key passed to TidLookup");
     249           0 :             return nullptr;
     250             :         }
     251         884 :         auto oIter = oTidLookup.find(pszKey);
     252         884 :         if (oIter == oTidLookup.end())
     253             :         {
     254           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     255             :                      "Unknown key %s passed to TidLookup", pszKey);
     256           0 :             return nullptr;
     257             :         }
     258         884 :         return oIter->second;
     259             :     }
     260             : 
     261         521 :     void AddFieldDefinitions(NodeVector oArcLineTypes)
     262             :     {
     263        1542 :         for (NodeVector::const_iterator it = oFields.begin();
     264        2563 :              it != oFields.end(); ++it)
     265             :         {
     266        1021 :             if (*it == nullptr)
     267           0 :                 continue;
     268        2042 :             const char *psName = CPLGetXMLValue(
     269        2042 :                 *it, "Name", CPLGetXMLValue(*it, "IlisMeta16:Name", nullptr));
     270        1021 :             if (psName == nullptr)
     271           0 :                 continue;
     272        2042 :             const char *psTypeRef = CPLGetXMLValue(
     273        1021 :                 *it, "Type.REF",
     274        1021 :                 CPLGetXMLValue(*it, "IlisMeta16:Type.ili:ref", nullptr));
     275        1021 :             if (psTypeRef == nullptr)         // Assoc Role
     276         137 :                 AddField(psName, OFTString);  // TODO: numeric?
     277             :             else
     278             :             {
     279         884 :                 const CPLXMLNode *psElementNode = TidLookup(psTypeRef);
     280         884 :                 if (psElementNode == nullptr)
     281           0 :                     continue;
     282         884 :                 const char *typeName = psElementNode->pszValue;
     283         884 :                 CPLDebug("OGR_ILI", "AddFieldDefinitions typename '%s'",
     284             :                          typeName);
     285         884 :                 if (EQUAL(typeName, "IlisMeta07.ModelData.TextType") ||
     286         712 :                     EQUAL(typeName, "IlisMeta16:TextType"))
     287             :                 {  // Kind Text,MText
     288         236 :                     AddField(psName, OFTString);
     289             :                 }
     290         648 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.EnumType") ||
     291         616 :                          EQUAL(typeName, "IlisMeta16:EnumType"))
     292             :                 {
     293          62 :                     AddField(psName,
     294          62 :                              (iliVersion == 1) ? OFTInteger : OFTString);
     295             :                 }
     296         586 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.BooleanType") ||
     297         586 :                          EQUAL(typeName, "IlisMeta16:BooleanType"))
     298             :                 {
     299           1 :                     AddField(psName, OFTString);  //??
     300             :                 }
     301         585 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.NumType") ||
     302         315 :                          EQUAL(typeName, "IlisMeta16:NumType"))
     303             :                 {  //// Unit INTERLIS.ANYUNIT, INTERLIS.TIME, INTERLIS.h,
     304             :                     /// INTERLIS.min, INTERLIS.s, INTERLIS.M, INTERLIS.d
     305         321 :                     AddField(psName, OFTReal);
     306             :                 }
     307         264 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.BlackboxType") ||
     308         262 :                          EQUAL(typeName, "IlisMeta16:BlackboxType"))
     309             :                 {
     310           6 :                     AddField(psName, OFTString);
     311             :                 }
     312         258 :                 else if (EQUAL(typeName,
     313         251 :                                "IlisMeta07.ModelData.FormattedType") ||
     314         251 :                          EQUAL(typeName, "IlisMeta16:FormattedType"))
     315             :                 {
     316          36 :                     AddField(psName, GetFormattedType(*it));
     317             :                 }
     318         222 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.MultiValue") ||
     319         147 :                          EQUAL(typeName, "IlisMeta16:MultiValue"))
     320             :                 {
     321             :                     // min -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Min
     322             :                     // max -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Max
     323         104 :                     const char *psClassRef = CPLGetXMLValue(
     324             :                         psElementNode, "BaseType.REF",
     325             :                         CPLGetXMLValue(psElementNode,
     326             :                                        "IlisMeta16:BaseType.ili:ref", nullptr));
     327         104 :                     if (psClassRef)
     328             :                     {
     329             :                         IliClass *psParentClass =
     330         104 :                             oClasses[oTidLookup[psClassRef]];
     331         104 :                         poStructFieldInfos[psName] = psParentClass->GetName();
     332         104 :                         CPLDebug("OGR_ILI",
     333             :                                  "Register table %s for struct field '%s'",
     334         208 :                                  poStructFieldInfos[psName].c_str(), psName);
     335             :                         /* Option: Embed fields if max == 1
     336             :                         CPLDebug( "OGR_ILI", "Adding embedded struct members of
     337             :                         MultiValue field '%s' from Class %s", psName,
     338             :                         psClassRef);
     339             :                         AddFieldDefinitions(psParentClass->oFields);
     340             :                         */
     341         104 :                     }
     342             :                 }
     343         118 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.CoordType") ||
     344          46 :                          EQUAL(typeName, "IlisMeta16:CoordType"))
     345             :                 {
     346          77 :                     AddCoord(psName, psElementNode);
     347             :                 }
     348          41 :                 else if (EQUAL(typeName, "IlisMeta07.ModelData.LineType") ||
     349           1 :                          EQUAL(typeName, "IlisMeta16:LineType"))
     350             :                 {
     351          41 :                     const char *psKind = CPLGetXMLValue(
     352             :                         psElementNode, "Kind",
     353             :                         CPLGetXMLValue(psElementNode, "IlisMeta16:Kind", ""));
     354          41 :                     poGeomFieldInfos[psName].iliGeomType = psKind;
     355             :                     bool isLinearType =
     356          41 :                         (std::find(oArcLineTypes.begin(), oArcLineTypes.end(),
     357          41 :                                    psElementNode) == oArcLineTypes.end());
     358             :                     bool linearGeom =
     359          41 :                         isLinearType || CPLTestBool(CPLGetConfigOption(
     360          41 :                                             "OGR_STROKE_CURVE", "FALSE"));
     361          41 :                     OGRwkbGeometryType multiLineType =
     362          41 :                         linearGeom ? wkbMultiLineString : wkbMultiCurve;
     363          41 :                     OGRwkbGeometryType polyType =
     364          41 :                         linearGeom ? wkbPolygon : wkbCurvePolygon;
     365          41 :                     if (iliVersion == 1)
     366             :                     {
     367          33 :                         if (EQUAL(psKind, "Area"))
     368             :                         {
     369             :                             CPLString lineLayerName =
     370          15 :                                 GetName() + CPLString("_") + psName;
     371           5 :                             AddGeomTable(lineLayerName, psName, multiLineType);
     372             : 
     373             :                             // Add geometry field for polygonized areas
     374           5 :                             AddGeomField(psName, wkbPolygon);
     375             : 
     376             :                             // We add the area helper point geometry after
     377             :                             // polygon for better behavior of clients with
     378             :                             // limited multi geometry support
     379             :                             CPLString areaPointGeomName =
     380          15 :                                 psName + CPLString("__Point");
     381           5 :                             AddCoord(areaPointGeomName, psElementNode);
     382             :                         }
     383          28 :                         else if (EQUAL(psKind, "Surface"))
     384             :                         {
     385             :                             CPLString geomLayerName =
     386          27 :                                 GetName() + CPLString("_") + psName;
     387           9 :                             AddGeomTable(geomLayerName, psName, multiLineType,
     388             :                                          true);
     389           9 :                             AddGeomField(psName, polyType);
     390             :                         }
     391             :                         else
     392             :                         {  // Polyline, DirectedPolyline
     393          19 :                             AddGeomField(psName, multiLineType);
     394             :                         }
     395             :                     }
     396             :                     else
     397             :                     {
     398           8 :                         if (EQUAL(psKind, "Area") || EQUAL(psKind, "Surface"))
     399             :                         {
     400           2 :                             AddGeomField(psName, polyType);
     401             :                         }
     402             :                         else
     403             :                         {  // Polyline, DirectedPolyline
     404           6 :                             AddGeomField(psName, multiLineType);
     405             :                         }
     406          41 :                     }
     407             :                 }
     408             :                 else
     409             :                 {
     410             :                     // ClassRefType
     411           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
     412             :                              "Field '%s' of class %s has unsupported type %s",
     413             :                              psName, GetName(), typeName);
     414             :                 }
     415             :             }
     416             :         }
     417         521 :     }
     418             : 
     419         521 :     FeatureDefnInfo tableDefs()
     420             :     {
     421         521 :         FeatureDefnInfo poLayerInfo;
     422         521 :         if (!hasDerivedClasses && !isEmbedded())
     423             :         {
     424         326 :             poLayerInfo.SetTableDefn(poTableDefn);
     425         326 :             poLayerInfo.poGeomFieldInfos = poGeomFieldInfos;
     426             :         }
     427         521 :         return poLayerInfo;
     428             :     }
     429             : 
     430             :   private:
     431             :     CPL_DISALLOW_COPY_ASSIGN(IliClass)
     432             : };
     433             : 
     434       26402 : ImdReader::ImdReader(int iliVersionIn)
     435             :     : iliVersion(iliVersionIn), mainTopicName("OGR"), codeBlank('_'),
     436       26402 :       codeUndefined('@'), codeContinue('\\')
     437             : {
     438       26402 : }
     439             : 
     440       26402 : ImdReader::~ImdReader()
     441             : {
     442       26402 : }
     443             : 
     444          16 : void ImdReader::ReadModel(const char *pszFilename)
     445             : {
     446          16 :     CPLDebug("OGR_ILI", "Reading model '%s'", pszFilename);
     447             : 
     448          16 :     CPLXMLNode *psRootNode = CPLParseXMLFile(pszFilename);
     449          16 :     if (psRootNode == nullptr)
     450           0 :         return;
     451             :     CPLXMLNode *psSectionNode =
     452          16 :         CPLGetXMLNode(psRootNode, "=TRANSFER.DATASECTION");
     453          16 :     if (psSectionNode == nullptr)
     454             :         psSectionNode =
     455           1 :             CPLGetXMLNode(psRootNode, "=ili:transfer.ili:datasection");
     456          16 :     if (psSectionNode == nullptr)
     457             :     {
     458           0 :         CPLDestroyXMLNode(psRootNode);
     459           0 :         return;
     460             :     }
     461             : 
     462          32 :     StrNodeMap oTidLookup; /* for fast lookup of REF relations */
     463          32 :     ClassesMap oClasses;
     464          32 :     NodeCountMap oAxisCount;
     465          32 :     NodeVector oArcLineTypes;
     466             : 
     467        7500 :     const auto TidLookup = [&oTidLookup](const char *pszKey)
     468             :     {
     469        2500 :         if (pszKey == nullptr)
     470             :         {
     471           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     472             :                      "Null key passed to TidLookup");
     473           0 :             return static_cast<CPLXMLNode *>(nullptr);
     474             :         }
     475        2500 :         auto oIter = oTidLookup.find(pszKey);
     476        2500 :         if (oIter == oTidLookup.end())
     477             :         {
     478           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     479             :                      "Unknown key %s passed to TidLookup", pszKey);
     480           0 :             return static_cast<CPLXMLNode *>(nullptr);
     481             :         }
     482        2500 :         return static_cast<CPLXMLNode *>(oIter->second);
     483          16 :     };
     484             : 
     485             :     /* Fill TID lookup map and IliClasses lookup map */
     486          16 :     CPLXMLNode *psModel = psSectionNode->psChild;
     487          67 :     while (psModel != nullptr)
     488             :     {
     489             : #ifdef DEBUG_VERBOSE
     490             :         const char *modelName = CPLGetXMLValue(
     491             :             psModel, "BID", CPLGetXMLValue(psModel, "ili:bid", nullptr));
     492             :         CPLDebug("OGR_ILI", "Model: '%s'", modelName);
     493             : #endif
     494             : 
     495        9124 :         for (CPLXMLNode *psEntry = psModel->psChild; psEntry != nullptr;
     496        9073 :              psEntry = psEntry->psNext)
     497             :         {
     498        9073 :             if (psEntry->eType != CXT_Attribute)  // ignore BID
     499             :             {
     500             : #ifdef DEBUG_VERBOSE
     501             :                 CPLDebug("OGR_ILI", "Node tag: '%s'", psEntry->pszValue);
     502             : #endif
     503             :                 const char *psTID =
     504        9022 :                     CPLGetXMLValue(psEntry, "TID",
     505             :                                    CPLGetXMLValue(psEntry, "ili:tid", nullptr));
     506        9022 :                 if (psTID != nullptr)
     507        6952 :                     oTidLookup[psTID] = psEntry;
     508             : 
     509        9022 :                 if (EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Model") ||
     510        8986 :                     EQUAL(psEntry->pszValue, "IlisMeta16:Model"))
     511             :                 {
     512         102 :                     IliModelInfo modelInfo;
     513             :                     modelInfo.name = CPLGetXMLValue(
     514             :                         psEntry, "Name",
     515          51 :                         CPLGetXMLValue(psEntry, "IlisMeta16:Name", "OGR"));
     516             :                     modelInfo.version = CPLGetXMLValue(
     517             :                         psEntry, "Version",
     518          51 :                         CPLGetXMLValue(psEntry, "IlisMeta16:iliVersion", ""));
     519             :                     // "1", "2.3", "2.4"
     520          51 :                     modelInfo.uri = CPLGetXMLValue(psEntry, "At", "");
     521          51 :                     modelInfos.push_back(std::move(modelInfo));
     522             : 
     523             :                     CPLXMLNode *psFormatNode =
     524          51 :                         CPLGetXMLNode(psEntry, "ili1Format");
     525          51 :                     if (psFormatNode != nullptr)
     526             :                     {
     527          13 :                         psFormatNode = psFormatNode->psChild;
     528          13 :                         codeBlank = static_cast<char>(atoi(
     529             :                             CPLGetXMLValue(psFormatNode, "blankCode", "95")));
     530          13 :                         codeUndefined = static_cast<char>(atoi(CPLGetXMLValue(
     531             :                             psFormatNode, "undefinedCode", "64")));
     532          13 :                         codeContinue = static_cast<char>(atoi(CPLGetXMLValue(
     533             :                             psFormatNode, "continueCode", "92")));
     534          51 :                     }
     535             :                 }
     536        8971 :                 else if (EQUAL(psEntry->pszValue,
     537        8938 :                                "IlisMeta07.ModelData.SubModel") ||
     538        8938 :                          EQUAL(psEntry->pszValue, "IlisMeta16:SubModel"))
     539             :                 {
     540             :                     mainBasketName = CPLGetXMLValue(
     541             :                         psEntry, "TID",
     542          47 :                         CPLGetXMLValue(psEntry, "ili:tid", "OGR"));
     543          47 :                     mainTopicName = CPLGetXMLValue(
     544             :                         psEntry, "Name",
     545          47 :                         CPLGetXMLValue(psEntry, "IlisMeta16:Name", "OGR"));
     546             :                 }
     547        8924 :                 else if (EQUAL(psEntry->pszValue,
     548        8511 :                                "IlisMeta07.ModelData.Class") ||
     549        8511 :                          EQUAL(psEntry->pszValue, "IlisMeta16:Class"))
     550             :                 {
     551         521 :                     CPLDebug("OGR_ILI", "Class name: '%s'", psTID);
     552         521 :                     const auto &modelVersion = modelInfos.back().version;
     553         521 :                     oClasses[psEntry] =
     554             :                         new IliClass(psEntry, iliVersion, modelVersion,
     555         521 :                                      oTidLookup, oClasses, oAxisCount);
     556             :                 }
     557             :             }
     558             :         }
     559             : 
     560             :         // 2nd pass: add fields via TransferElement entries & role associations
     561        9124 :         for (CPLXMLNode *psEntry = psModel->psChild; psEntry != nullptr;
     562        9073 :              psEntry = psEntry->psNext)
     563             :         {
     564        9073 :             if (psEntry->eType != CXT_Attribute)  // ignore BID
     565             :             {
     566             : #ifdef DEBUG_VERBOSE
     567             :                 CPLDebug("OGR_ILI", "Node tag: '%s'", psEntry->pszValue);
     568             : #endif
     569        9022 :                 if (iliVersion == 1 &&
     570        3925 :                     EQUAL(psEntry->pszValue,
     571             :                           "IlisMeta07.ModelData.Ili1TransferElement"))
     572             :                 {
     573         150 :                     const char *psClassRef = CPLGetXMLValue(
     574             :                         psEntry, "Ili1TransferClass.REF", nullptr);
     575             :                     const char *psElementRef =
     576         150 :                         CPLGetXMLValue(psEntry, "Ili1RefAttr.REF", nullptr);
     577         150 :                     if (psClassRef == nullptr || psElementRef == nullptr)
     578           0 :                         continue;
     579             :                     int iOrderPos =
     580         150 :                         atoi(CPLGetXMLValue(psEntry, "Ili1RefAttr.ORDER_POS",
     581             :                                             "0")) -
     582         150 :                         1;
     583         150 :                     auto tidClassRef = TidLookup(psClassRef);
     584         150 :                     if (tidClassRef == nullptr)
     585           0 :                         continue;
     586         150 :                     auto classesIter = oClasses.find(tidClassRef);
     587         150 :                     if (classesIter == oClasses.end())
     588           0 :                         continue;
     589         150 :                     IliClass *psParentClass = classesIter->second;
     590         150 :                     CPLXMLNode *psElementNode = TidLookup(psElementRef);
     591         150 :                     if (psElementNode == nullptr)
     592           0 :                         continue;
     593         150 :                     psParentClass->AddFieldNode(psElementNode, iOrderPos);
     594             :                 }
     595        8872 :                 else if (EQUAL(psEntry->pszValue,
     596        8178 :                                "IlisMeta07.ModelData.TransferElement") ||
     597        8178 :                          EQUAL(psEntry->pszValue, "IlisMeta16:TransferElement"))
     598             :                 {
     599         963 :                     const char *psClassRef = CPLGetXMLValue(
     600             :                         psEntry, "TransferClass.REF",
     601             :                         CPLGetXMLValue(psEntry,
     602             :                                        "IlisMeta16:TransferClass.ili:ref",
     603             :                                        nullptr));
     604         963 :                     const char *psElementRef = CPLGetXMLValue(
     605             :                         psEntry, "TransferElement.REF",
     606             :                         CPLGetXMLValue(psEntry,
     607             :                                        "IlisMeta16:TransferElement.ili:ref",
     608             :                                        nullptr));
     609         963 :                     if (psClassRef == nullptr || psElementRef == nullptr)
     610           0 :                         continue;
     611             :                     int iOrderPos =
     612         963 :                         atoi(CPLGetXMLValue(
     613             :                             psEntry, "TransferElement.ORDER_POS",
     614             :                             CPLGetXMLValue(
     615             :                                 psEntry,
     616             :                                 "IlisMeta16:TransferElement.ili:order_pos",
     617             :                                 "0"))) -
     618         963 :                         1;
     619         963 :                     auto tidClassRef = TidLookup(psClassRef);
     620         963 :                     if (tidClassRef == nullptr)
     621           0 :                         continue;
     622         963 :                     auto classesIter = oClasses.find(tidClassRef);
     623         963 :                     if (classesIter == oClasses.end())
     624           0 :                         continue;
     625         963 :                     IliClass *psParentClass = classesIter->second;
     626         963 :                     CPLXMLNode *psElementNode = TidLookup(psElementRef);
     627         963 :                     if (psElementNode == nullptr)
     628           0 :                         continue;
     629         963 :                     psParentClass->AddFieldNode(psElementNode, iOrderPos);
     630             :                 }
     631        7909 :                 else if (EQUAL(psEntry->pszValue,
     632        7873 :                                "IlisMeta07.ModelData.Role") ||
     633        7873 :                          EQUAL(psEntry->pszValue, "IlisMeta16:Role"))
     634             :                 {
     635          86 :                     const char *psRefParent = CPLGetXMLValue(
     636             :                         psEntry, "Association.REF",
     637             :                         CPLGetXMLValue(psEntry,
     638             :                                        "IlisMeta16:Association.ili:ref",
     639             :                                        nullptr));
     640          86 :                     if (psRefParent == nullptr)
     641           0 :                         continue;
     642             :                     int iOrderPos =
     643          86 :                         atoi(CPLGetXMLValue(
     644             :                             psEntry, "Association.ORDER_POS",
     645             :                             CPLGetXMLValue(
     646             :                                 psEntry, "IlisMeta16:Association.ili:order_pos",
     647             :                                 "0"))) -
     648          86 :                         1;
     649          86 :                     auto tidClassRef = TidLookup(psRefParent);
     650          86 :                     if (tidClassRef == nullptr)
     651           0 :                         continue;
     652          86 :                     auto classesIter = oClasses.find(tidClassRef);
     653          86 :                     if (classesIter == oClasses.end())
     654           0 :                         continue;
     655          86 :                     IliClass *psParentClass = classesIter->second;
     656          86 :                     if (psParentClass)
     657          86 :                         psParentClass->AddRoleNode(psEntry, iOrderPos);
     658             :                 }
     659        7823 :                 else if (EQUAL(psEntry->pszValue,
     660        7715 :                                "IlisMeta07.ModelData.AxisSpec") ||
     661        7715 :                          EQUAL(psEntry->pszValue, "IlisMeta16:AxisSpec"))
     662             :                 {
     663         142 :                     const char *psClassRef = CPLGetXMLValue(
     664             :                         psEntry, "CoordType.REF",
     665             :                         CPLGetXMLValue(psEntry, "IlisMeta16:CoordType.ili:ref",
     666             :                                        nullptr));
     667         142 :                     if (psClassRef == nullptr)
     668           0 :                         continue;
     669             :                     // int iOrderPos = atoi(
     670             :                     //     CPLGetXMLValue( psEntry, "Axis.ORDER_POS", "0" ))-1;
     671         142 :                     CPLXMLNode *psCoordTypeNode = TidLookup(psClassRef);
     672         142 :                     if (psCoordTypeNode == nullptr)
     673           0 :                         continue;
     674         142 :                     oAxisCount[psCoordTypeNode] += 1;
     675             :                 }
     676        7681 :                 else if (EQUAL(psEntry->pszValue,
     677        7635 :                                "IlisMeta07.ModelData.LinesForm") ||
     678        7635 :                          EQUAL(psEntry->pszValue, "IlisMeta16:LinesForm"))
     679             :                 {
     680         118 :                     const char *psLineForm = CPLGetXMLValue(
     681             :                         psEntry, "LineForm.REF",
     682             :                         CPLGetXMLValue(psEntry, "IlisMeta16:LineForm.ili:ref",
     683             :                                        nullptr));
     684         118 :                     if (psLineForm != nullptr &&
     685         118 :                         EQUAL(psLineForm, "INTERLIS.ARCS"))
     686             :                     {
     687          46 :                         const char *psElementRef = CPLGetXMLValue(
     688             :                             psEntry, "LineType.REF",
     689             :                             CPLGetXMLValue(psEntry,
     690             :                                            "IlisMeta16:LineType.ili:ref",
     691             :                                            nullptr));
     692          46 :                         CPLXMLNode *psElementNode = TidLookup(psElementRef);
     693          46 :                         if (psElementNode == nullptr)
     694           0 :                             continue;
     695          46 :                         oArcLineTypes.push_back(psElementNode);
     696             :                     }
     697             :                 }
     698             :             }
     699             :         }
     700             : 
     701          51 :         psModel = psModel->psNext;
     702             :     }
     703             : 
     704             :     // Last model is main model
     705          32 :     const CPLString mainModelName = modelInfos.back().name;
     706          32 :     const CPLString modelVersion = modelInfos.back().version;
     707          16 :     CPLDebug("OGR_ILI", "mainModelName: '%s' version: '%s'",
     708             :              mainModelName.c_str(), modelVersion.c_str());
     709             : 
     710             :     /* Analyze class inheritance & add fields to class table defn */
     711         537 :     for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end();
     712         521 :          ++it)
     713             :     {
     714             : #ifdef DEBUG_VERBOSE
     715             :         CPLDebug("OGR_ILI", "Class: '%s'", it->second->GetName());
     716             : #endif
     717        2084 :         const char *psRefSuper = CPLGetXMLValue(
     718         521 :             it->first, "Super.REF",
     719         521 :             CPLGetXMLValue(it->first, "IlisMeta16:Super.ili:ref", nullptr));
     720         521 :         if (psRefSuper)
     721             :         {
     722         675 :             if (oTidLookup.find(psRefSuper) != oTidLookup.end() &&
     723         450 :                 oClasses.find(oTidLookup[psRefSuper]) != oClasses.end())
     724             :             {
     725         225 :                 oClasses[oTidLookup[psRefSuper]]->hasDerivedClasses = true;
     726             :             }
     727             :             else
     728             :             {
     729           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     730             :                          "Couldn't reference super class '%s'", psRefSuper);
     731             :             }
     732             :         }
     733         521 :         it->second->InitFieldDefinitions();
     734         521 :         it->second->AddFieldDefinitions(oArcLineTypes);
     735             :     }
     736             : 
     737             :     /* Filter relevant classes */
     738         537 :     for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end();
     739         521 :          ++it)
     740             :     {
     741         521 :         const char *className = it->second->GetIliName();
     742        1042 :         FeatureDefnInfo oClassInfo = it->second->tableDefs();
     743         521 :         bool include = EQUAL(modelVersion, "2.4")
     744         521 :                            ? STARTS_WITH(className, mainModelName.c_str())
     745         413 :                            : !STARTS_WITH_CI(className, "INTERLIS.");
     746         521 :         if (include && oClassInfo.GetTableDefnRef())
     747             :         {
     748          71 :             featureDefnInfos.push_back(oClassInfo);
     749             :         }
     750             :     }
     751             : 
     752         537 :     for (ClassesMap::iterator it = oClasses.begin(); it != oClasses.end(); ++it)
     753             :     {
     754         521 :         delete it->second;
     755             :     }
     756             : 
     757          16 :     CPLDestroyXMLNode(psRootNode);
     758             : }
     759             : 
     760           0 : FeatureDefnInfo ImdReader::GetFeatureDefnInfo(const char *pszLayerName)
     761             : {
     762           0 :     FeatureDefnInfo featureDefnInfo;
     763           0 :     for (FeatureDefnInfos::const_iterator it = featureDefnInfos.begin();
     764           0 :          it != featureDefnInfos.end(); ++it)
     765             :     {
     766           0 :         OGRFeatureDefn *fdefn = it->GetTableDefnRef();
     767           0 :         if (EQUAL(fdefn->GetName(), pszLayerName))
     768           0 :             featureDefnInfo = *it;
     769             :     }
     770           0 :     return featureDefnInfo;
     771             : }

Generated by: LCOV version 1.14