Coverage Report

Created: 2025-06-09 08:44

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