Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/ili/ili2reader.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Interlis 2 Reader
4
 * Purpose:  Implementation of ILI2Reader class.
5
 * Author:   Markus Schnider, Sourcepole AG
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG
9
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "ili2readerp.h"
15
#include "ogr_ili2.h"
16
#include "cpl_conv.h"
17
#include "cpl_string.h"
18
19
#include "ili2reader.h"
20
21
using namespace std;
22
23
//
24
// constants
25
//
26
static const char *const ILI2_TID = "TID";
27
static const XMLCh xmlch_ILI2_TID[] = {'T', 'I', 'D', '\0'};
28
static const XMLCh xmlch_ILI2_TID_NS[] = {'i', 'l', 'i', ':',
29
                                          't', 'i', 'd', '\0'};
30
static const XMLCh ILI2_REF[] = {'R', 'E', 'F', '\0'};
31
static const XMLCh ILI2_REF_NS[] = {'i', 'l', 'i', ':', 'r', 'e', 'f', '\0'};
32
33
constexpr int ILI2_STRING_TYPE = 0;
34
constexpr int ILI2_COORD_TYPE = 1;
35
constexpr int ILI2_ARC_TYPE = 2;
36
constexpr int ILI2_POLYLINE_TYPE = 4;
37
constexpr int ILI2_BOUNDARY_TYPE = 8;
38
constexpr int ILI2_AREA_TYPE = 16;  // also SURFACE
39
constexpr int ILI2_GEOMCOLL_TYPE = 32;
40
41
static const char *const ILI2_COORD = "COORD";
42
static const char *const ILI2_ARC = "ARC";
43
static const char *const ILI2_POLYLINE = "POLYLINE";
44
static const char *const ILI2_BOUNDARY = "BOUNDARY";
45
static const char *const ILI2_AREA = "AREA";
46
static const char *const ILI2_SURFACE = "SURFACE";
47
48
namespace gdal
49
{
50
namespace ili2
51
{
52
//
53
// helper functions
54
//
55
int cmpStr(const string &s1, const string &s2)
56
1.27M
{
57
1.27M
    string::const_iterator p1 = s1.begin();
58
1.27M
    string::const_iterator p2 = s2.begin();
59
60
3.89M
    while (p1 != s1.end() && p2 != s2.end())
61
3.85M
    {
62
3.85M
        if (CPLToupper(static_cast<unsigned char>(*p1)) !=
63
3.85M
            CPLToupper(static_cast<unsigned char>(*p2)))
64
1.23M
            return (CPLToupper(static_cast<unsigned char>(*p1)) <
65
1.23M
                    CPLToupper(static_cast<unsigned char>(*p2)))
66
1.23M
                       ? -1
67
1.23M
                       : 1;
68
2.61M
        ++p1;
69
2.61M
        ++p2;
70
2.61M
    }
71
72
40.9k
    return (s2.size() == s1.size()) ? 0 : (s1.size() < s2.size()) ? -1 : 1;
73
1.27M
}
74
75
string ltrim(const string &tmpstr)
76
59.2k
{
77
59.2k
    size_t i = 0;
78
124k
    while (i < tmpstr.length() && (tmpstr[i] == ' ' || tmpstr[i] == '\t' ||
79
59.4k
                                   tmpstr[i] == '\r' || tmpstr[i] == '\n'))
80
64.8k
        ++i;
81
59.2k
    return i > 0 ? tmpstr.substr(i, tmpstr.length() - i) : tmpstr;
82
59.2k
}
83
84
string rtrim(const string &tmpstr)
85
59.2k
{
86
59.2k
    if (tmpstr.empty())
87
4.62k
        return tmpstr;
88
54.5k
    size_t i = tmpstr.length() - 1U;
89
55.3k
    while (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' ||
90
54.8k
           tmpstr[i] == '\n')
91
818
        --i;
92
54.5k
    return i < tmpstr.length() - 1 ? tmpstr.substr(0, i + 1) : tmpstr;
93
59.2k
}
94
95
string trim(const string &tmpstr)
96
59.2k
{
97
59.2k
    auto ret = ltrim(tmpstr);
98
59.2k
    ret = rtrim(ret);
99
59.2k
    return ret;
100
59.2k
}
101
}  // namespace ili2
102
}  // namespace gdal
103
104
using namespace gdal::ili2;
105
106
static int getGeometryTypeOfElem(DOMElement *elem)
107
167k
{
108
167k
    int type = ILI2_STRING_TYPE;
109
167k
    if (elem == nullptr)
110
0
        return type;
111
167k
    char *pszTagName = XMLString::transcode(elem->getTagName());
112
113
167k
    if (elem->getNodeType() == DOMNode::ELEMENT_NODE)
114
167k
    {
115
167k
        if (cmpStr(ILI2_COORD, pszTagName) == 0)
116
77
        {
117
77
            type = ILI2_COORD_TYPE;
118
77
        }
119
167k
        else if (cmpStr(ILI2_ARC, pszTagName) == 0)
120
0
        {
121
0
            type = ILI2_ARC_TYPE;
122
0
        }
123
167k
        else if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
124
87
        {
125
87
            type = ILI2_POLYLINE_TYPE;
126
87
        }
127
167k
        else if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
128
0
        {
129
0
            type = ILI2_BOUNDARY_TYPE;
130
0
        }
131
167k
        else if (cmpStr(ILI2_AREA, pszTagName) == 0)
132
0
        {
133
0
            type = ILI2_AREA_TYPE;
134
0
        }
135
167k
        else if (cmpStr(ILI2_SURFACE, pszTagName) == 0)
136
423
        {
137
423
            type = ILI2_AREA_TYPE;
138
423
        }
139
167k
    }
140
167k
    XMLString::release(&pszTagName);
141
167k
    return type;
142
167k
}
143
144
static char *getObjValue(DOMElement *elem)
145
64.2k
{
146
64.2k
    DOMNode *child = elem->getFirstChild();
147
64.2k
    if ((child != nullptr) && (child->getNodeType() == DOMNode::TEXT_NODE))
148
51.2k
    {
149
51.2k
        return CPLStrdup(transcode(child->getNodeValue()));
150
51.2k
    }
151
152
13.0k
    return nullptr;
153
64.2k
}
154
155
static char *getREFValue(DOMElement *elem)
156
13.0k
{
157
13.0k
    const XMLCh *val = elem->hasAttribute(ILI2_REF)
158
13.0k
                           ? elem->getAttribute(ILI2_REF)
159
13.0k
                           : elem->getAttribute(ILI2_REF_NS);
160
13.0k
    CPLString osREFValue(transcode(val));
161
13.0k
    return CPLStrdup(osREFValue);
162
13.0k
}
163
164
static OGRPoint *getPoint(DOMElement *elem)
165
5.45k
{
166
    // elem -> COORD (or ARC)
167
5.45k
    DOMElement *coordElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
168
5.45k
    if (coordElem == nullptr)
169
1
        return nullptr;
170
5.45k
    OGRPoint *pt = new OGRPoint();
171
172
16.3k
    while (coordElem != nullptr)
173
10.9k
    {
174
10.9k
        char *pszTagName = XMLString::transcode(coordElem->getTagName());
175
10.9k
        char *pszObjValue = getObjValue(coordElem);
176
10.9k
        if (pszObjValue)
177
10.9k
        {
178
10.9k
            if (cmpStr("C1", pszTagName) == 0)
179
5.45k
                pt->setX(CPLAtof(pszObjValue));
180
5.45k
            else if (cmpStr("C2", pszTagName) == 0)
181
5.45k
                pt->setY(CPLAtof(pszObjValue));
182
0
            else if (cmpStr("C3", pszTagName) == 0)
183
0
                pt->setZ(CPLAtof(pszObjValue));
184
10.9k
        }
185
10.9k
        CPLFree(pszObjValue);
186
10.9k
        XMLString::release(&pszTagName);
187
10.9k
        coordElem = dynamic_cast<DOMElement *>(coordElem->getNextSibling());
188
10.9k
    }
189
5.45k
    pt->flattenTo2D();
190
5.45k
    return pt;
191
5.45k
}
192
193
OGRCircularString *ILI2Reader::getArc(DOMElement *elem)
194
0
{
195
    // previous point -> start point
196
0
    auto elemPrev = dynamic_cast<DOMElement *>(elem->getPreviousSibling());
197
0
    if (elemPrev == nullptr)
198
0
        return nullptr;
199
0
    OGRPoint *ptStart = getPoint(elemPrev);  // COORD or ARC
200
0
    if (ptStart == nullptr)
201
0
        return nullptr;
202
203
    // elem -> ARC
204
0
    OGRCircularString *arc = new OGRCircularString();
205
    // end point
206
0
    OGRPoint *ptEnd = new OGRPoint();
207
    // point on the arc
208
0
    OGRPoint *ptOnArc = new OGRPoint();
209
    // double radius = 0; // radius
210
211
0
    DOMElement *arcElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
212
0
    while (arcElem != nullptr)
213
0
    {
214
0
        char *pszTagName = XMLString::transcode(arcElem->getTagName());
215
0
        char *pszObjValue = getObjValue(arcElem);
216
0
        if (pszObjValue)
217
0
        {
218
0
            if (cmpStr("C1", pszTagName) == 0)
219
0
                ptEnd->setX(CPLAtof(pszObjValue));
220
0
            else if (cmpStr("C2", pszTagName) == 0)
221
0
                ptEnd->setY(CPLAtof(pszObjValue));
222
0
            else if (cmpStr("C3", pszTagName) == 0)
223
0
                ptEnd->setZ(CPLAtof(pszObjValue));
224
0
            else if (cmpStr("A1", pszTagName) == 0)
225
0
                ptOnArc->setX(CPLAtof(pszObjValue));
226
0
            else if (cmpStr("A2", pszTagName) == 0)
227
0
                ptOnArc->setY(CPLAtof(pszObjValue));
228
0
            else if (cmpStr("A3", pszTagName) == 0)
229
0
                ptOnArc->setZ(CPLAtof(pszObjValue));
230
0
            else if (cmpStr("R", pszTagName) == 0)
231
0
            {
232
                // radius = CPLAtof(pszObjValue);
233
0
            }
234
0
        }
235
0
        CPLFree(pszObjValue);
236
0
        XMLString::release(&pszTagName);
237
0
        arcElem = dynamic_cast<DOMElement *>(arcElem->getNextSibling());
238
0
    }
239
0
    arc->addPoint(ptStart);
240
0
    arc->addPoint(ptOnArc);
241
0
    arc->addPoint(ptEnd);
242
0
    delete ptStart;
243
0
    delete ptOnArc;
244
0
    delete ptEnd;
245
0
    return arc;
246
0
}
247
248
static OGRCompoundCurve *getPolyline(DOMElement *elem)
249
267
{
250
    // elem -> POLYLINE
251
267
    OGRCompoundCurve *ogrCurve = new OGRCompoundCurve();
252
267
    OGRLineString *ls = new OGRLineString();
253
254
267
    DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
255
5.83k
    while (lineElem != nullptr)
256
5.56k
    {
257
5.56k
        char *pszTagName = XMLString::transcode(lineElem->getTagName());
258
5.56k
        if (cmpStr(ILI2_COORD, pszTagName) == 0)
259
5.41k
        {
260
5.41k
            OGRPoint *poPoint = getPoint(lineElem);
261
5.41k
            if (poPoint)
262
5.41k
            {
263
5.41k
                ls->addPoint(poPoint);
264
5.41k
                delete poPoint;
265
5.41k
            }
266
5.41k
        }
267
151
        else if (cmpStr(ILI2_ARC, pszTagName) == 0)
268
0
        {
269
            // Finish line and start arc
270
0
            if (ls->getNumPoints() > 1)
271
0
            {
272
0
                ogrCurve->addCurveDirectly(ls);
273
0
                ls = new OGRLineString();
274
0
            }
275
0
            else
276
0
            {
277
0
                ls->empty();
278
0
            }
279
0
            OGRCircularString *arc = new OGRCircularString();
280
            // end point
281
0
            OGRPoint *ptEnd = new OGRPoint();
282
            // point on the arc
283
0
            OGRPoint *ptOnArc = new OGRPoint();
284
            // radius
285
            // double radius = 0;
286
287
0
            DOMElement *arcElem =
288
0
                dynamic_cast<DOMElement *>(lineElem->getFirstChild());
289
0
            while (arcElem != nullptr)
290
0
            {
291
0
                char *pszTagName2 = XMLString::transcode(arcElem->getTagName());
292
0
                char *pszObjValue = getObjValue(arcElem);
293
0
                if (pszObjValue)
294
0
                {
295
0
                    if (cmpStr("C1", pszTagName2) == 0)
296
0
                        ptEnd->setX(CPLAtof(pszObjValue));
297
0
                    else if (cmpStr("C2", pszTagName2) == 0)
298
0
                        ptEnd->setY(CPLAtof(pszObjValue));
299
0
                    else if (cmpStr("C3", pszTagName2) == 0)
300
0
                        ptEnd->setZ(CPLAtof(pszObjValue));
301
0
                    else if (cmpStr("A1", pszTagName2) == 0)
302
0
                        ptOnArc->setX(CPLAtof(pszObjValue));
303
0
                    else if (cmpStr("A2", pszTagName2) == 0)
304
0
                        ptOnArc->setY(CPLAtof(pszObjValue));
305
0
                    else if (cmpStr("A3", pszTagName2) == 0)
306
0
                        ptOnArc->setZ(CPLAtof(pszObjValue));
307
0
                    else if (cmpStr("R", pszTagName2) == 0)
308
0
                    {
309
                        // radius = CPLAtof(pszObjValue);
310
0
                    }
311
0
                }
312
0
                CPLFree(pszObjValue);
313
0
                XMLString::release(&pszTagName2);
314
315
0
                arcElem = dynamic_cast<DOMElement *>(arcElem->getNextSibling());
316
0
            }
317
318
0
            auto elemPrev =
319
0
                dynamic_cast<DOMElement *>(lineElem->getPreviousSibling());
320
0
            if (elemPrev)
321
0
            {
322
0
                OGRPoint *ptStart = getPoint(elemPrev);  // COORD or ARC
323
0
                if (ptStart)
324
0
                    arc->addPoint(ptStart);
325
0
                delete ptStart;
326
0
            }
327
0
            arc->addPoint(ptOnArc);
328
0
            arc->addPoint(ptEnd);
329
0
            ogrCurve->addCurveDirectly(arc);
330
331
            // Add arc endpoint as next start point, if COORD sequence follows.
332
0
            DOMElement *nextElem =
333
0
                dynamic_cast<DOMElement *>(lineElem->getNextSibling());
334
0
            if (nextElem)
335
0
            {
336
0
                char *nextTagName =
337
0
                    XMLString::transcode(nextElem->getTagName());
338
0
                if (cmpStr(ILI2_COORD, nextTagName) == 0)
339
0
                {
340
0
                    ls->addPoint(ptEnd);
341
0
                }
342
0
                XMLString::release(&nextTagName);
343
0
            }
344
345
0
            delete ptEnd;
346
0
            delete ptOnArc;
347
0
        } /* else { // TODO: StructureValue in Polyline not yet supported
348
        } */
349
5.56k
        XMLString::release(&pszTagName);
350
351
5.56k
        lineElem = dynamic_cast<DOMElement *>(lineElem->getNextSibling());
352
5.56k
    }
353
354
267
    if (ls->getNumPoints() > 1)
355
265
    {
356
265
        ogrCurve->addCurveDirectly(ls);
357
265
    }
358
2
    else
359
2
    {
360
2
        delete ls;
361
2
    }
362
267
    return ogrCurve;
363
267
}
364
365
static OGRCompoundCurve *getBoundary(DOMElement *elem)
366
228
{
367
368
228
    DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
369
228
    if (lineElem != nullptr)
370
227
    {
371
227
        char *pszTagName = XMLString::transcode(lineElem->getTagName());
372
227
        if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
373
227
        {
374
227
            XMLString::release(&pszTagName);
375
227
            return getPolyline(lineElem);
376
227
        }
377
0
        XMLString::release(&pszTagName);
378
0
    }
379
380
1
    return new OGRCompoundCurve();
381
228
}
382
383
static OGRCurvePolygon *getPolygon(DOMElement *elem)
384
200
{
385
200
    OGRCurvePolygon *pg = new OGRCurvePolygon();
386
387
200
    DOMElement *boundaryElem =
388
200
        dynamic_cast<DOMElement *>(elem->getFirstChild());  // outer boundary
389
428
    while (boundaryElem != nullptr)
390
228
    {
391
228
        char *pszTagName = XMLString::transcode(boundaryElem->getTagName());
392
228
        if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
393
228
        {
394
228
            OGRCompoundCurve *poCC = getBoundary(boundaryElem);
395
228
            if (pg->addRingDirectly(poCC) != OGRERR_NONE)
396
0
            {
397
0
                delete poCC;
398
0
            }
399
228
        }
400
228
        XMLString::release(&pszTagName);
401
228
        boundaryElem = dynamic_cast<DOMElement *>(
402
228
            boundaryElem->getNextSibling());  // inner boundaries
403
228
    }
404
405
200
    return pg;
406
200
}
407
408
OGRGeometry *ILI2Reader::getGeometry(DOMElement *elem, int type)
409
274
{
410
274
    OGRGeometryCollection *gm = new OGRGeometryCollection();
411
412
274
    DOMElement *childElem = elem;
413
274
    while (childElem != nullptr)
414
274
    {
415
274
        char *pszTagName = XMLString::transcode(childElem->getTagName());
416
274
        switch (type)
417
274
        {
418
34
            case ILI2_COORD_TYPE:
419
34
                if (cmpStr(ILI2_COORD, pszTagName) == 0)
420
34
                {
421
34
                    delete gm;
422
34
                    XMLString::release(&pszTagName);
423
34
                    return getPoint(childElem);
424
34
                }
425
0
                break;
426
0
            case ILI2_ARC_TYPE:
427
                // is it possible here? It have to be a ARC or COORD before
428
                // (getPreviousSibling)
429
0
                if (cmpStr(ILI2_ARC, pszTagName) == 0)
430
0
                {
431
0
                    delete gm;
432
0
                    XMLString::release(&pszTagName);
433
0
                    return getArc(childElem);
434
0
                }
435
0
                break;
436
40
            case ILI2_POLYLINE_TYPE:
437
40
                if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
438
40
                {
439
40
                    delete gm;
440
40
                    XMLString::release(&pszTagName);
441
40
                    return getPolyline(childElem);
442
40
                }
443
0
                break;
444
0
            case ILI2_BOUNDARY_TYPE:
445
0
                if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
446
0
                {
447
0
                    delete gm;
448
0
                    XMLString::release(&pszTagName);
449
0
                    return getPolyline(childElem);
450
0
                }
451
0
                break;
452
200
            case ILI2_AREA_TYPE:
453
200
                if ((cmpStr(ILI2_AREA, pszTagName) == 0) ||
454
200
                    (cmpStr(ILI2_SURFACE, pszTagName) == 0))
455
200
                {
456
200
                    delete gm;
457
200
                    XMLString::release(&pszTagName);
458
200
                    return getPolygon(childElem);
459
200
                }
460
0
                break;
461
0
            default:
462
0
                if (type >= ILI2_GEOMCOLL_TYPE)
463
0
                {
464
0
                    int subType = getGeometryTypeOfElem(childElem);  //????
465
0
                    OGRGeometry *poSubGeom = getGeometry(childElem, subType);
466
0
                    if (poSubGeom)
467
0
                        gm->addGeometryDirectly(poSubGeom);
468
0
                }
469
0
                break;
470
274
        }
471
0
        XMLString::release(&pszTagName);
472
473
        // GEOMCOLL
474
0
        childElem = dynamic_cast<DOMElement *>(childElem->getNextSibling());
475
0
    }
476
477
0
    return gm;
478
274
}
479
480
int ILI2Reader::ReadModel(OGRILI2DataSource *poDS, ImdReader *poImdReader,
481
                          const char *modelFilename)
482
0
{
483
0
    poImdReader->ReadModel(modelFilename);
484
0
    for (FeatureDefnInfos::const_iterator it =
485
0
             poImdReader->featureDefnInfos.begin();
486
0
         it != poImdReader->featureDefnInfos.end(); ++it)
487
0
    {
488
0
        m_listLayer.push_back(std::make_unique<OGRILI2Layer>(
489
0
            it->GetTableDefnRef(), it->poGeomFieldInfos, poDS));
490
0
    }
491
0
    return 0;
492
0
}
493
494
// Detect field name of value element
495
static char *fieldName(DOMElement *elem)
496
81.4k
{
497
81.4k
    DOMNode *node = elem;
498
81.4k
    if (getGeometryTypeOfElem(elem))
499
274
    {
500
274
        int depth = 0;  // Depth of value elem node
501
1.64k
        for (node = elem; node; node = node->getParentNode())
502
1.37k
            ++depth;
503
        // Field name is on level 4
504
274
        node = elem;
505
548
        for (int d = 0; d < depth - 4; ++d)
506
274
            node = node->getParentNode();
507
274
    }
508
81.4k
    if (node == nullptr)
509
0
    {
510
0
        CPLError(CE_Failure, CPLE_AssertionFailed, "node == NULL");
511
0
        return CPLStrdup("***bug***");
512
0
    }
513
81.4k
    return CPLStrdup(transcode(node->getNodeName()));
514
81.4k
}
515
516
void ILI2Reader::setFieldDefn(OGRFeatureDefn *featureDef, DOMElement *elem)
517
2.06k
{
518
2.06k
    int type = 0;
519
    // recursively search children
520
2.06k
    for (DOMNode *childNode = elem->getFirstChild();
521
6.56k
         type == 0 && childNode &&
522
4.50k
         childNode->getNodeType() == DOMNode::ELEMENT_NODE;
523
4.50k
         childNode = childNode->getNextSibling())
524
4.50k
    {
525
4.50k
        DOMElement *childElem = dynamic_cast<DOMElement *>(childNode);
526
4.50k
        CPLAssert(childElem);
527
4.50k
        type = getGeometryTypeOfElem(childElem);
528
4.50k
        if (type == 0)
529
4.46k
        {
530
4.46k
            if (childElem->getFirstChild() &&
531
3.39k
                childElem->getFirstChild()->getNodeType() ==
532
3.39k
                    DOMNode::ELEMENT_NODE)
533
551
            {
534
551
                setFieldDefn(featureDef, childElem);
535
551
            }
536
3.91k
            else
537
3.91k
            {
538
3.91k
                char *fName = fieldName(childElem);
539
3.91k
                if (featureDef->GetFieldIndex(fName) == -1)
540
3.75k
                {
541
3.75k
                    CPLDebug("OGR_ILI", "AddFieldDefn: %s", fName);
542
3.75k
                    OGRFieldDefn oFieldDefn(fName, OFTString);
543
3.75k
                    featureDef->AddFieldDefn(&oFieldDefn);
544
3.75k
                }
545
3.91k
                CPLFree(fName);
546
3.91k
            }
547
4.46k
        }
548
4.50k
    }
549
2.06k
}
550
551
void ILI2Reader::SetFieldValues(OGRFeature *feature, DOMElement *elem)
552
22.6k
{
553
22.6k
    int type = 0;
554
    // recursively search children
555
22.6k
    for (DOMNode *childNode = elem->getFirstChild();
556
104k
         type == 0 && childNode &&
557
81.6k
         childNode->getNodeType() == DOMNode::ELEMENT_NODE;
558
81.6k
         childNode = childNode->getNextSibling())
559
81.6k
    {
560
81.6k
        DOMElement *childElem = dynamic_cast<DOMElement *>(childNode);
561
81.6k
        CPLAssert(childElem);
562
81.6k
        type = getGeometryTypeOfElem(childElem);
563
81.6k
        if (type == 0)
564
81.3k
        {
565
81.3k
            if (childElem->getFirstChild() &&
566
44.8k
                childElem->getFirstChild()->getNodeType() ==
567
44.8k
                    DOMNode::ELEMENT_NODE)
568
4.05k
            {
569
4.05k
                SetFieldValues(feature, childElem);
570
4.05k
            }
571
77.3k
            else
572
77.3k
            {
573
77.3k
                char *fName = fieldName(childElem);
574
77.3k
                int fIndex = feature->GetFieldIndex(fName);
575
77.3k
                if (fIndex != -1)
576
53.3k
                {
577
53.3k
                    char *objVal = getObjValue(childElem);
578
53.3k
                    if (objVal == nullptr)
579
13.0k
                        objVal = getREFValue(childElem);  // only to try
580
53.3k
                    feature->SetField(fIndex, objVal);
581
53.3k
                    CPLFree(objVal);
582
53.3k
                }
583
23.9k
                else
584
23.9k
                {
585
23.9k
                    CPLDebug("OGR_ILI", "Attribute '%s' not found", fName);
586
23.9k
                    m_missAttrs.push_back(fName);
587
23.9k
                }
588
77.3k
                CPLFree(fName);
589
77.3k
            }
590
81.3k
        }
591
275
        else
592
275
        {
593
275
            char *fName = fieldName(childElem);
594
275
            int fIndex = feature->GetGeomFieldIndex(fName);
595
275
            OGRGeometry *geom = getGeometry(childElem, type);
596
275
            if (geom)
597
274
            {
598
274
                if (fIndex == -1)
599
274
                {  // Unknown model
600
274
                    feature->SetGeometryDirectly(geom);
601
274
                }
602
0
                else
603
0
                {
604
0
                    OGRwkbGeometryType geomType =
605
0
                        feature->GetGeomFieldDefnRef(fIndex)->GetType();
606
0
                    if (geomType == wkbMultiLineString ||
607
0
                        geomType == wkbPolygon)
608
0
                    {
609
0
                        feature->SetGeomFieldDirectly(
610
0
                            fIndex, geom->getLinearGeometry());
611
0
                        delete geom;
612
0
                    }
613
0
                    else
614
0
                    {
615
0
                        feature->SetGeomFieldDirectly(fIndex, geom);
616
0
                    }
617
0
                }
618
274
            }
619
275
            CPLFree(fName);
620
275
        }
621
81.6k
    }
622
22.6k
}
623
624
//
625
// ILI2Reader
626
//
627
IILI2Reader::~IILI2Reader()
628
674
{
629
674
}
630
631
ILI2Reader::ILI2Reader()
632
674
    : m_pszFilename(nullptr), m_poILI2Handler(nullptr), m_poSAXReader(nullptr),
633
674
      m_bReadStarted(FALSE), m_bXercesInitialized(false)
634
674
{
635
674
    SetupParser();
636
674
}
637
638
ILI2Reader::~ILI2Reader()
639
674
{
640
674
    CPLFree(m_pszFilename);
641
642
674
    CleanupParser();
643
644
674
    if (m_bXercesInitialized)
645
674
        OGRDeinitializeXerces();
646
674
}
647
648
void ILI2Reader::SetSourceFile(const char *pszFilename)
649
674
{
650
674
    CPLFree(m_pszFilename);
651
674
    m_pszFilename = CPLStrdup(pszFilename);
652
674
}
653
654
int ILI2Reader::SetupParser()
655
674
{
656
657
674
    if (!m_bXercesInitialized)
658
674
    {
659
674
        if (!OGRInitializeXerces())
660
0
            return FALSE;
661
674
        m_bXercesInitialized = true;
662
674
    }
663
664
    // Cleanup any old parser.
665
674
    if (m_poSAXReader != nullptr)
666
0
        CleanupParser();
667
668
    // Create and initialize parser.
669
674
    m_poSAXReader = XMLReaderFactory::createXMLReader();
670
671
674
    m_poILI2Handler = new ILI2Handler(this);
672
673
674
    m_poSAXReader->setContentHandler(m_poILI2Handler);
674
674
    m_poSAXReader->setErrorHandler(m_poILI2Handler);
675
674
    m_poSAXReader->setLexicalHandler(m_poILI2Handler);
676
674
    m_poSAXReader->setEntityResolver(m_poILI2Handler);
677
674
    m_poSAXReader->setDTDHandler(m_poILI2Handler);
678
674
    m_poSAXReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
679
674
                              true);
680
681
    // No Validation
682
674
    m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
683
674
    m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
684
685
674
    m_bReadStarted = FALSE;
686
687
674
    return TRUE;
688
674
}
689
690
void ILI2Reader::CleanupParser()
691
674
{
692
674
    if (m_poSAXReader == nullptr)
693
0
        return;
694
695
674
    delete m_poSAXReader;
696
674
    m_poSAXReader = nullptr;
697
698
674
    delete m_poILI2Handler;
699
674
    m_poILI2Handler = nullptr;
700
701
674
    m_bReadStarted = FALSE;
702
674
}
703
704
int ILI2Reader::SaveClasses(const char *pszFile = nullptr)
705
674
{
706
707
    // Add logic later to determine reasonable default schema file.
708
674
    if (pszFile == nullptr)
709
0
        return FALSE;
710
711
674
    VSILFILE *fp = VSIFOpenL(pszFile, "rb");
712
674
    if (fp == nullptr)
713
0
        return FALSE;
714
715
674
    InputSource *is = OGRCreateXercesInputSource(fp);
716
717
    // parse and create layers and features
718
674
    try
719
674
    {
720
674
        CPLDebug("OGR_ILI", "Parsing %s", pszFile);
721
674
        m_poSAXReader->parse(*is);
722
674
        VSIFCloseL(fp);
723
674
        OGRDestroyXercesInputSource(is);
724
674
    }
725
674
    catch (const DOMException &toCatch)
726
674
    {
727
        // Can happen with createElement() in ILI2Handler::startElement()
728
4
        CPLError(CE_Failure, CPLE_AppDefined, "DOMException: %s",
729
4
                 transcode(toCatch.getMessage()).c_str());
730
4
        VSIFCloseL(fp);
731
4
        OGRDestroyXercesInputSource(is);
732
4
        return FALSE;
733
4
    }
734
674
    catch (const SAXException &toCatch)
735
674
    {
736
0
        CPLError(CE_Failure, CPLE_AppDefined, "Parsing failed: %s",
737
0
                 transcode(toCatch.getMessage()).c_str());
738
0
        VSIFCloseL(fp);
739
0
        OGRDestroyXercesInputSource(is);
740
0
        return FALSE;
741
0
    }
742
743
670
    if (!m_missAttrs.empty())
744
148
    {
745
148
        m_missAttrs.sort();
746
148
        m_missAttrs.unique();
747
148
        string attrs = "";
748
148
        list<string>::const_iterator it;
749
2.52k
        for (it = m_missAttrs.begin(); it != m_missAttrs.end(); ++it)
750
2.37k
            attrs += *it + ", ";
751
752
148
        CPLError(CE_Warning, CPLE_NotSupported,
753
148
                 "Failed to add new definition to existing layers, attributes "
754
148
                 "not saved: %s",
755
148
                 attrs.c_str());
756
148
    }
757
758
670
    return TRUE;
759
674
}
760
761
std::vector<std::unique_ptr<OGRLayer>> &ILI2Reader::GetLayers()
762
1.61k
{
763
1.61k
    return m_listLayer;
764
1.61k
}
765
766
int ILI2Reader::GetLayerCount() const
767
928
{
768
928
    return static_cast<int>(m_listLayer.size());
769
928
}
770
771
OGRLayer *ILI2Reader::GetLayer(const char *pszName)
772
18.6k
{
773
206k
    for (auto it = m_listLayer.rbegin(); it != m_listLayer.rend(); ++it)
774
204k
    {
775
204k
        const OGRFeatureDefn *fDef = (*it)->GetLayerDefn();
776
204k
        if (cmpStr(fDef->GetName(), pszName) == 0)
777
17.1k
        {
778
17.1k
            return it->get();
779
17.1k
        }
780
204k
    }
781
1.51k
    return nullptr;
782
18.6k
}
783
784
int ILI2Reader::AddFeature(DOMElement *elem)
785
18.6k
{
786
18.6k
    CPLString osName(transcode(elem->getTagName()));
787
    // CPLDebug( "OGR_ILI", "Reading layer: %s", osName.c_str() );
788
789
    // test if this layer exist
790
18.6k
    OGRILI2Layer *curLayer = cpl::down_cast<OGRILI2Layer *>(GetLayer(osName));
791
18.6k
    const bool needsNewLayer = (curLayer == nullptr);
792
18.6k
    std::unique_ptr<OGRILI2Layer> newLayer;
793
794
    // add a layer
795
18.6k
    if (needsNewLayer)
796
1.51k
    {
797
1.51k
        CPLDebug("OGR_ILI", "Adding layer: %s", osName.c_str());
798
1.51k
        OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn(osName);
799
1.51k
        poFeatureDefn->SetGeomType(wkbUnknown);
800
1.51k
        GeomFieldInfos oGeomFieldInfos;
801
1.51k
        newLayer = std::make_unique<OGRILI2Layer>(poFeatureDefn,
802
1.51k
                                                  oGeomFieldInfos, nullptr);
803
1.51k
        curLayer = newLayer.get();
804
1.51k
    }
805
806
    // the feature and field definition
807
18.6k
    OGRFeatureDefn *featureDef = curLayer->GetLayerDefn();
808
18.6k
    if (needsNewLayer)
809
1.51k
    {
810
        // add TID field
811
1.51k
        OGRFieldDefn ofieldDefn(ILI2_TID, OFTString);
812
1.51k
        featureDef->AddFieldDefn(&ofieldDefn);
813
814
1.51k
        setFieldDefn(featureDef, elem);
815
1.51k
    }
816
817
    // add the features
818
18.6k
    OGRFeature *feature = new OGRFeature(featureDef);
819
820
    // assign TID
821
18.6k
    int fIndex = feature->GetFieldIndex(ILI2_TID);
822
18.6k
    if (fIndex != -1)
823
18.6k
    {
824
18.6k
        const XMLCh *val = elem->hasAttribute(xmlch_ILI2_TID)
825
18.6k
                               ? elem->getAttribute(xmlch_ILI2_TID)
826
18.6k
                               : elem->getAttribute(xmlch_ILI2_TID_NS);
827
18.6k
        feature->SetField(fIndex, transcode(val).c_str());
828
18.6k
    }
829
0
    else
830
0
    {
831
0
        CPLDebug("OGR_ILI", "'%s' not found", ILI2_TID);
832
0
    }
833
834
18.6k
    SetFieldValues(feature, elem);
835
18.6k
    curLayer->AddFeature(feature);
836
837
18.6k
    if (needsNewLayer)
838
1.51k
        m_listLayer.push_back(std::move(newLayer));
839
840
18.6k
    return 0;
841
18.6k
}
842
843
IILI2Reader *CreateILI2Reader()
844
674
{
845
674
    return new ILI2Reader();
846
674
}
847
848
void DestroyILI2Reader(IILI2Reader *reader)
849
38.9k
{
850
38.9k
    if (reader)
851
674
        delete reader;
852
38.9k
}