Coverage Report

Created: 2026-06-30 08:33

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