Coverage Report

Created: 2026-08-14 09:29

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.13M
{
57
1.13M
    string::const_iterator p1 = s1.begin();
58
1.13M
    string::const_iterator p2 = s2.begin();
59
60
3.63M
    while (p1 != s1.end() && p2 != s2.end())
61
3.59M
    {
62
3.59M
        if (CPLToupper(static_cast<unsigned char>(*p1)) !=
63
3.59M
            CPLToupper(static_cast<unsigned char>(*p2)))
64
1.09M
            return (CPLToupper(static_cast<unsigned char>(*p1)) <
65
1.09M
                    CPLToupper(static_cast<unsigned char>(*p2)))
66
1.09M
                       ? -1
67
1.09M
                       : 1;
68
2.49M
        ++p1;
69
2.49M
        ++p2;
70
2.49M
    }
71
72
38.9k
    return (s2.size() == s1.size()) ? 0 : (s1.size() < s2.size()) ? -1 : 1;
73
1.13M
}
74
75
string ltrim(const string &tmpstr)
76
53.5k
{
77
53.5k
    size_t i = 0;
78
113k
    while (i < tmpstr.length() && (tmpstr[i] == ' ' || tmpstr[i] == '\t' ||
79
53.7k
                                   tmpstr[i] == '\r' || tmpstr[i] == '\n'))
80
59.9k
        ++i;
81
53.5k
    return i > 0 ? tmpstr.substr(i, tmpstr.length() - i) : tmpstr;
82
53.5k
}
83
84
string rtrim(const string &tmpstr)
85
53.5k
{
86
53.5k
    if (tmpstr.empty())
87
4.27k
        return tmpstr;
88
49.2k
    size_t i = tmpstr.length() - 1U;
89
50.0k
    while (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' ||
90
49.5k
           tmpstr[i] == '\n')
91
758
        --i;
92
49.2k
    return i < tmpstr.length() - 1 ? tmpstr.substr(0, i + 1) : tmpstr;
93
53.5k
}
94
95
string trim(const string &tmpstr)
96
53.5k
{
97
53.5k
    auto ret = ltrim(tmpstr);
98
53.5k
    ret = rtrim(ret);
99
53.5k
    return ret;
100
53.5k
}
101
}  // namespace ili2
102
}  // namespace gdal
103
104
using namespace gdal::ili2;
105
106
static int getGeometryTypeOfElem(DOMElement *elem)
107
149k
{
108
149k
    int type = ILI2_STRING_TYPE;
109
149k
    if (elem == nullptr)
110
0
        return type;
111
149k
    char *pszTagName = XMLString::transcode(elem->getTagName());
112
113
149k
    if (elem->getNodeType() == DOMNode::ELEMENT_NODE)
114
149k
    {
115
149k
        if (cmpStr(ILI2_COORD, pszTagName) == 0)
116
77
        {
117
77
            type = ILI2_COORD_TYPE;
118
77
        }
119
149k
        else if (cmpStr(ILI2_ARC, pszTagName) == 0)
120
0
        {
121
0
            type = ILI2_ARC_TYPE;
122
0
        }
123
149k
        else if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
124
87
        {
125
87
            type = ILI2_POLYLINE_TYPE;
126
87
        }
127
149k
        else if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
128
0
        {
129
0
            type = ILI2_BOUNDARY_TYPE;
130
0
        }
131
149k
        else if (cmpStr(ILI2_AREA, pszTagName) == 0)
132
0
        {
133
0
            type = ILI2_AREA_TYPE;
134
0
        }
135
149k
        else if (cmpStr(ILI2_SURFACE, pszTagName) == 0)
136
403
        {
137
403
            type = ILI2_AREA_TYPE;
138
403
        }
139
149k
    }
140
149k
    XMLString::release(&pszTagName);
141
149k
    return type;
142
149k
}
143
144
static char *getObjValue(DOMElement *elem)
145
57.7k
{
146
57.7k
    DOMNode *child = elem->getFirstChild();
147
57.7k
    if ((child != nullptr) && (child->getNodeType() == DOMNode::TEXT_NODE))
148
46.0k
    {
149
46.0k
        return CPLStrdup(transcode(child->getNodeValue()));
150
46.0k
    }
151
152
11.7k
    return nullptr;
153
57.7k
}
154
155
static char *getREFValue(DOMElement *elem)
156
11.7k
{
157
11.7k
    const XMLCh *val = elem->hasAttribute(ILI2_REF)
158
11.7k
                           ? elem->getAttribute(ILI2_REF)
159
11.7k
                           : elem->getAttribute(ILI2_REF_NS);
160
11.7k
    CPLString osREFValue(transcode(val));
161
11.7k
    return CPLStrdup(osREFValue);
162
11.7k
}
163
164
static OGRPoint *getPoint(DOMElement *elem)
165
5.33k
{
166
    // elem -> COORD (or ARC)
167
5.33k
    DOMElement *coordElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
168
5.33k
    if (coordElem == nullptr)
169
1
        return nullptr;
170
5.33k
    OGRPoint *pt = new OGRPoint();
171
172
16.0k
    while (coordElem != nullptr)
173
10.6k
    {
174
10.6k
        char *pszTagName = XMLString::transcode(coordElem->getTagName());
175
10.6k
        char *pszObjValue = getObjValue(coordElem);
176
10.6k
        if (pszObjValue)
177
10.6k
        {
178
10.6k
            if (cmpStr("C1", pszTagName) == 0)
179
5.33k
                pt->setX(CPLAtof(pszObjValue));
180
5.33k
            else if (cmpStr("C2", pszTagName) == 0)
181
5.33k
                pt->setY(CPLAtof(pszObjValue));
182
0
            else if (cmpStr("C3", pszTagName) == 0)
183
0
                pt->setZ(CPLAtof(pszObjValue));
184
10.6k
        }
185
10.6k
        CPLFree(pszObjValue);
186
10.6k
        XMLString::release(&pszTagName);
187
10.6k
        coordElem = dynamic_cast<DOMElement *>(coordElem->getNextSibling());
188
10.6k
    }
189
5.33k
    pt->flattenTo2D();
190
5.33k
    return pt;
191
5.33k
}
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
254
{
250
    // elem -> POLYLINE
251
254
    OGRCompoundCurve *ogrCurve = new OGRCompoundCurve();
252
254
    OGRLineString *ls = new OGRLineString();
253
254
254
    DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
255
5.69k
    while (lineElem != nullptr)
256
5.44k
    {
257
5.44k
        char *pszTagName = XMLString::transcode(lineElem->getTagName());
258
5.44k
        if (cmpStr(ILI2_COORD, pszTagName) == 0)
259
5.30k
        {
260
5.30k
            OGRPoint *poPoint = getPoint(lineElem);
261
5.30k
            if (poPoint)
262
5.30k
            {
263
5.30k
                ls->addPoint(poPoint);
264
5.30k
                delete poPoint;
265
5.30k
            }
266
5.30k
        }
267
137
        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.44k
        XMLString::release(&pszTagName);
350
351
5.44k
        lineElem = dynamic_cast<DOMElement *>(lineElem->getNextSibling());
352
5.44k
    }
353
354
254
    if (ls->getNumPoints() > 1)
355
251
    {
356
251
        ogrCurve->addCurveDirectly(ls);
357
251
    }
358
3
    else
359
3
    {
360
3
        delete ls;
361
3
    }
362
254
    return ogrCurve;
363
254
}
364
365
static OGRCompoundCurve *getBoundary(DOMElement *elem)
366
215
{
367
368
215
    DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
369
215
    if (lineElem != nullptr)
370
214
    {
371
214
        char *pszTagName = XMLString::transcode(lineElem->getTagName());
372
214
        if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
373
214
        {
374
214
            XMLString::release(&pszTagName);
375
214
            return getPolyline(lineElem);
376
214
        }
377
0
        XMLString::release(&pszTagName);
378
0
    }
379
380
1
    return new OGRCompoundCurve();
381
215
}
382
383
static OGRCurvePolygon *getPolygon(DOMElement *elem)
384
190
{
385
190
    OGRCurvePolygon *pg = new OGRCurvePolygon();
386
387
190
    DOMElement *boundaryElem =
388
190
        dynamic_cast<DOMElement *>(elem->getFirstChild());  // outer boundary
389
405
    while (boundaryElem != nullptr)
390
215
    {
391
215
        char *pszTagName = XMLString::transcode(boundaryElem->getTagName());
392
215
        if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
393
215
        {
394
215
            OGRCompoundCurve *poCC = getBoundary(boundaryElem);
395
215
            if (pg->addRingDirectly(poCC) != OGRERR_NONE)
396
0
            {
397
0
                delete poCC;
398
0
            }
399
215
        }
400
215
        XMLString::release(&pszTagName);
401
215
        boundaryElem = dynamic_cast<DOMElement *>(
402
215
            boundaryElem->getNextSibling());  // inner boundaries
403
215
    }
404
405
190
    return pg;
406
190
}
407
408
OGRGeometry *ILI2Reader::getGeometry(DOMElement *elem, int type)
409
264
{
410
264
    OGRGeometryCollection *gm = new OGRGeometryCollection();
411
412
264
    DOMElement *childElem = elem;
413
264
    while (childElem != nullptr)
414
264
    {
415
264
        char *pszTagName = XMLString::transcode(childElem->getTagName());
416
264
        switch (type)
417
264
        {
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
190
            case ILI2_AREA_TYPE:
453
190
                if ((cmpStr(ILI2_AREA, pszTagName) == 0) ||
454
190
                    (cmpStr(ILI2_SURFACE, pszTagName) == 0))
455
190
                {
456
190
                    delete gm;
457
190
                    XMLString::release(&pszTagName);
458
190
                    return getPolygon(childElem);
459
190
                }
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
264
        }
471
0
        XMLString::release(&pszTagName);
472
473
        // GEOMCOLL
474
0
        childElem = dynamic_cast<DOMElement *>(childElem->getNextSibling());
475
0
    }
476
477
0
    return gm;
478
264
}
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
72.4k
{
497
72.4k
    DOMNode *node = elem;
498
72.4k
    if (getGeometryTypeOfElem(elem))
499
264
    {
500
264
        int depth = 0;  // Depth of value elem node
501
1.58k
        for (node = elem; node; node = node->getParentNode())
502
1.32k
            ++depth;
503
        // Field name is on level 4
504
264
        node = elem;
505
528
        for (int d = 0; d < depth - 4; ++d)
506
264
            node = node->getParentNode();
507
264
    }
508
72.4k
    if (node == nullptr)
509
0
    {
510
0
        CPLError(CE_Failure, CPLE_AssertionFailed, "node == NULL");
511
0
        return CPLStrdup("***bug***");
512
0
    }
513
72.4k
    return CPLStrdup(transcode(node->getNodeName()));
514
72.4k
}
515
516
void ILI2Reader::setFieldDefn(OGRFeatureDefn *featureDef, DOMElement *elem)
517
1.90k
{
518
1.90k
    int type = 0;
519
    // recursively search children
520
1.90k
    for (DOMNode *childNode = elem->getFirstChild();
521
5.75k
         type == 0 && childNode &&
522
3.85k
         childNode->getNodeType() == DOMNode::ELEMENT_NODE;
523
3.85k
         childNode = childNode->getNextSibling())
524
3.85k
    {
525
3.85k
        DOMElement *childElem = dynamic_cast<DOMElement *>(childNode);
526
3.85k
        CPLAssert(childElem);
527
3.85k
        type = getGeometryTypeOfElem(childElem);
528
3.85k
        if (type == 0)
529
3.81k
        {
530
3.81k
            if (childElem->getFirstChild() &&
531
2.90k
                childElem->getFirstChild()->getNodeType() ==
532
2.90k
                    DOMNode::ELEMENT_NODE)
533
500
            {
534
500
                setFieldDefn(featureDef, childElem);
535
500
            }
536
3.31k
            else
537
3.31k
            {
538
3.31k
                char *fName = fieldName(childElem);
539
3.31k
                if (featureDef->GetFieldIndex(fName) == -1)
540
3.18k
                {
541
3.18k
                    CPLDebug("OGR_ILI", "AddFieldDefn: %s", fName);
542
3.18k
                    OGRFieldDefn oFieldDefn(fName, OFTString);
543
3.18k
                    featureDef->AddFieldDefn(&oFieldDefn);
544
3.18k
                }
545
3.31k
                CPLFree(fName);
546
3.31k
            }
547
3.81k
        }
548
3.85k
    }
549
1.90k
}
550
551
void ILI2Reader::SetFieldValues(OGRFeature *feature, DOMElement *elem)
552
20.8k
{
553
20.8k
    int type = 0;
554
    // recursively search children
555
20.8k
    for (DOMNode *childNode = elem->getFirstChild();
556
93.8k
         type == 0 && childNode &&
557
73.0k
         childNode->getNodeType() == DOMNode::ELEMENT_NODE;
558
72.9k
         childNode = childNode->getNextSibling())
559
72.9k
    {
560
72.9k
        DOMElement *childElem = dynamic_cast<DOMElement *>(childNode);
561
72.9k
        CPLAssert(childElem);
562
72.9k
        type = getGeometryTypeOfElem(childElem);
563
72.9k
        if (type == 0)
564
72.7k
        {
565
72.7k
            if (childElem->getFirstChild() &&
566
39.6k
                childElem->getFirstChild()->getNodeType() ==
567
39.6k
                    DOMNode::ELEMENT_NODE)
568
3.84k
            {
569
3.84k
                SetFieldValues(feature, childElem);
570
3.84k
            }
571
68.8k
            else
572
68.8k
            {
573
68.8k
                char *fName = fieldName(childElem);
574
68.8k
                int fIndex = feature->GetFieldIndex(fName);
575
68.8k
                if (fIndex != -1)
576
47.0k
                {
577
47.0k
                    char *objVal = getObjValue(childElem);
578
47.0k
                    if (objVal == nullptr)
579
11.7k
                        objVal = getREFValue(childElem);  // only to try
580
47.0k
                    feature->SetField(fIndex, objVal);
581
47.0k
                    CPLFree(objVal);
582
47.0k
                }
583
21.8k
                else
584
21.8k
                {
585
21.8k
                    CPLDebug("OGR_ILI", "Attribute '%s' not found", fName);
586
21.8k
                    m_missAttrs.push_back(fName);
587
21.8k
                }
588
68.8k
                CPLFree(fName);
589
68.8k
            }
590
72.7k
        }
591
265
        else
592
265
        {
593
265
            char *fName = fieldName(childElem);
594
265
            int fIndex = feature->GetGeomFieldIndex(fName);
595
265
            OGRGeometry *geom = getGeometry(childElem, type);
596
265
            if (geom)
597
264
            {
598
264
                if (fIndex == -1)
599
264
                {  // Unknown model
600
264
                    feature->SetGeometryDirectly(geom);
601
264
                }
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
264
            }
619
265
            CPLFree(fName);
620
265
        }
621
72.9k
    }
622
20.8k
}
623
624
//
625
// ILI2Reader
626
//
627
IILI2Reader::~IILI2Reader()
628
637
{
629
637
}
630
631
ILI2Reader::ILI2Reader()
632
637
    : m_pszFilename(nullptr), m_poILI2Handler(nullptr), m_poSAXReader(nullptr),
633
637
      m_bReadStarted(FALSE), m_bXercesInitialized(false)
634
637
{
635
637
    SetupParser();
636
637
}
637
638
ILI2Reader::~ILI2Reader()
639
637
{
640
637
    CPLFree(m_pszFilename);
641
642
637
    CleanupParser();
643
644
637
    if (m_bXercesInitialized)
645
637
        OGRDeinitializeXerces();
646
637
}
647
648
void ILI2Reader::SetSourceFile(const char *pszFilename)
649
637
{
650
637
    CPLFree(m_pszFilename);
651
637
    m_pszFilename = CPLStrdup(pszFilename);
652
637
}
653
654
int ILI2Reader::SetupParser()
655
637
{
656
657
637
    if (!m_bXercesInitialized)
658
637
    {
659
637
        if (!OGRInitializeXerces())
660
0
            return FALSE;
661
637
        m_bXercesInitialized = true;
662
637
    }
663
664
    // Cleanup any old parser.
665
637
    if (m_poSAXReader != nullptr)
666
0
        CleanupParser();
667
668
    // Create and initialize parser.
669
637
    m_poSAXReader = XMLReaderFactory::createXMLReader();
670
671
637
    m_poILI2Handler = new ILI2Handler(this);
672
673
637
    m_poSAXReader->setContentHandler(m_poILI2Handler);
674
637
    m_poSAXReader->setErrorHandler(m_poILI2Handler);
675
637
    m_poSAXReader->setLexicalHandler(m_poILI2Handler);
676
637
    m_poSAXReader->setEntityResolver(m_poILI2Handler);
677
637
    m_poSAXReader->setDTDHandler(m_poILI2Handler);
678
637
    m_poSAXReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
679
637
                              true);
680
681
    // No Validation
682
637
    m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
683
637
    m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
684
685
637
    m_bReadStarted = FALSE;
686
687
637
    return TRUE;
688
637
}
689
690
void ILI2Reader::CleanupParser()
691
637
{
692
637
    if (m_poSAXReader == nullptr)
693
0
        return;
694
695
637
    delete m_poSAXReader;
696
637
    m_poSAXReader = nullptr;
697
698
637
    delete m_poILI2Handler;
699
637
    m_poILI2Handler = nullptr;
700
701
637
    m_bReadStarted = FALSE;
702
637
}
703
704
int ILI2Reader::SaveClasses(const char *pszFile = nullptr)
705
637
{
706
707
    // Add logic later to determine reasonable default schema file.
708
637
    if (pszFile == nullptr)
709
0
        return FALSE;
710
711
637
    VSILFILE *fp = VSIFOpenL(pszFile, "rb");
712
637
    if (fp == nullptr)
713
0
        return FALSE;
714
715
637
    InputSource *is = OGRCreateXercesInputSource(fp);
716
717
    // parse and create layers and features
718
637
    try
719
637
    {
720
637
        CPLDebug("OGR_ILI", "Parsing %s", pszFile);
721
637
        m_poSAXReader->parse(*is);
722
637
        VSIFCloseL(fp);
723
637
        OGRDestroyXercesInputSource(is);
724
637
    }
725
637
    catch (const DOMException &toCatch)
726
637
    {
727
        // Can happen with createElement() in ILI2Handler::startElement()
728
4
        CPLError(CE_Failure, CPLE_AppDefined, "DOMException: %s\n",
729
4
                 transcode(toCatch.getMessage()).c_str());
730
4
        VSIFCloseL(fp);
731
4
        OGRDestroyXercesInputSource(is);
732
4
        return FALSE;
733
4
    }
734
637
    catch (const SAXException &toCatch)
735
637
    {
736
0
        CPLError(CE_Failure, CPLE_AppDefined, "Parsing failed: %s\n",
737
0
                 transcode(toCatch.getMessage()).c_str());
738
0
        VSIFCloseL(fp);
739
0
        OGRDestroyXercesInputSource(is);
740
0
        return FALSE;
741
0
    }
742
743
633
    if (!m_missAttrs.empty())
744
145
    {
745
145
        m_missAttrs.sort();
746
145
        m_missAttrs.unique();
747
145
        string attrs = "";
748
145
        list<string>::const_iterator it;
749
2.39k
        for (it = m_missAttrs.begin(); it != m_missAttrs.end(); ++it)
750
2.24k
            attrs += *it + ", ";
751
752
145
        CPLError(CE_Warning, CPLE_NotSupported,
753
145
                 "Failed to add new definition to existing layers, attributes "
754
145
                 "not saved: %s",
755
145
                 attrs.c_str());
756
145
    }
757
758
633
    return TRUE;
759
637
}
760
761
std::vector<std::unique_ptr<OGRLayer>> &ILI2Reader::GetLayers()
762
1.54k
{
763
1.54k
    return m_listLayer;
764
1.54k
}
765
766
int ILI2Reader::GetLayerCount() const
767
887
{
768
887
    return static_cast<int>(m_listLayer.size());
769
887
}
770
771
OGRLayer *ILI2Reader::GetLayer(const char *pszName)
772
16.9k
{
773
187k
    for (auto it = m_listLayer.rbegin(); it != m_listLayer.rend(); ++it)
774
186k
    {
775
186k
        const OGRFeatureDefn *fDef = (*it)->GetLayerDefn();
776
186k
        if (cmpStr(fDef->GetName(), pszName) == 0)
777
15.5k
        {
778
15.5k
            return it->get();
779
15.5k
        }
780
186k
    }
781
1.40k
    return nullptr;
782
16.9k
}
783
784
int ILI2Reader::AddFeature(DOMElement *elem)
785
16.9k
{
786
16.9k
    CPLString osName(transcode(elem->getTagName()));
787
    // CPLDebug( "OGR_ILI", "Reading layer: %s", osName.c_str() );
788
789
    // test if this layer exist
790
16.9k
    OGRILI2Layer *curLayer = cpl::down_cast<OGRILI2Layer *>(GetLayer(osName));
791
16.9k
    const bool needsNewLayer = (curLayer == nullptr);
792
16.9k
    std::unique_ptr<OGRILI2Layer> newLayer;
793
794
    // add a layer
795
16.9k
    if (needsNewLayer)
796
1.40k
    {
797
1.40k
        CPLDebug("OGR_ILI", "Adding layer: %s", osName.c_str());
798
1.40k
        OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn(osName);
799
1.40k
        poFeatureDefn->SetGeomType(wkbUnknown);
800
1.40k
        GeomFieldInfos oGeomFieldInfos;
801
1.40k
        newLayer = std::make_unique<OGRILI2Layer>(poFeatureDefn,
802
1.40k
                                                  oGeomFieldInfos, nullptr);
803
1.40k
        curLayer = newLayer.get();
804
1.40k
    }
805
806
    // the feature and field definition
807
16.9k
    OGRFeatureDefn *featureDef = curLayer->GetLayerDefn();
808
16.9k
    if (needsNewLayer)
809
1.40k
    {
810
        // add TID field
811
1.40k
        OGRFieldDefn ofieldDefn(ILI2_TID, OFTString);
812
1.40k
        featureDef->AddFieldDefn(&ofieldDefn);
813
814
1.40k
        setFieldDefn(featureDef, elem);
815
1.40k
    }
816
817
    // add the features
818
16.9k
    OGRFeature *feature = new OGRFeature(featureDef);
819
820
    // assign TID
821
16.9k
    int fIndex = feature->GetFieldIndex(ILI2_TID);
822
16.9k
    if (fIndex != -1)
823
16.9k
    {
824
16.9k
        const XMLCh *val = elem->hasAttribute(xmlch_ILI2_TID)
825
16.9k
                               ? elem->getAttribute(xmlch_ILI2_TID)
826
16.9k
                               : elem->getAttribute(xmlch_ILI2_TID_NS);
827
16.9k
        feature->SetField(fIndex, transcode(val).c_str());
828
16.9k
    }
829
0
    else
830
0
    {
831
0
        CPLDebug("OGR_ILI", "'%s' not found", ILI2_TID);
832
0
    }
833
834
16.9k
    SetFieldValues(feature, elem);
835
16.9k
    curLayer->AddFeature(feature);
836
837
16.9k
    if (needsNewLayer)
838
1.40k
        m_listLayer.push_back(std::move(newLayer));
839
840
16.9k
    return 0;
841
16.9k
}
842
843
IILI2Reader *CreateILI2Reader()
844
637
{
845
637
    return new ILI2Reader();
846
637
}
847
848
void DestroyILI2Reader(IILI2Reader *reader)
849
37.6k
{
850
37.6k
    if (reader)
851
637
        delete reader;
852
37.6k
}