Coverage Report

Created: 2025-07-23 09:13

/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
631k
{
54
631k
    string::const_iterator p1 = s1.begin();
55
631k
    string::const_iterator p2 = s2.begin();
56
57
2.49M
    while (p1 != s1.end() && p2 != s2.end())
58
2.47M
    {
59
2.47M
        if (CPLToupper(static_cast<unsigned char>(*p1)) !=
60
2.47M
            CPLToupper(static_cast<unsigned char>(*p2)))
61
612k
            return (CPLToupper(static_cast<unsigned char>(*p1)) <
62
612k
                    CPLToupper(static_cast<unsigned char>(*p2)))
63
612k
                       ? -1
64
612k
                       : 1;
65
1.86M
        ++p1;
66
1.86M
        ++p2;
67
1.86M
    }
68
69
19.2k
    return (s2.size() == s1.size()) ? 0 : (s1.size() < s2.size()) ? -1 : 1;
70
631k
}
71
72
string ltrim(const string &tmpstr)
73
22.6k
{
74
22.6k
    size_t i = 0;
75
31.5k
    while (i < tmpstr.length() && (tmpstr[i] == ' ' || tmpstr[i] == '\t' ||
76
30.7k
                                   tmpstr[i] == '\r' || tmpstr[i] == '\n'))
77
8.94k
        ++i;
78
22.6k
    return i > 0 ? tmpstr.substr(i, tmpstr.length() - i) : tmpstr;
79
22.6k
}
80
81
string rtrim(const string &tmpstr)
82
22.6k
{
83
22.6k
    if (tmpstr.empty())
84
833
        return tmpstr;
85
21.7k
    size_t i = tmpstr.length() - 1U;
86
22.0k
    while (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' ||
87
22.0k
           tmpstr[i] == '\n')
88
290
        --i;
89
21.7k
    return i < tmpstr.length() - 1 ? tmpstr.substr(0, i + 1) : tmpstr;
90
22.6k
}
91
92
string trim(const string &tmpstr)
93
22.6k
{
94
22.6k
    auto ret = ltrim(tmpstr);
95
22.6k
    ret = rtrim(ret);
96
22.6k
    return ret;
97
22.6k
}
98
}  // namespace ili2
99
}  // namespace gdal
100
101
using namespace gdal::ili2;
102
103
static int getGeometryTypeOfElem(DOMElement *elem)
104
82.1k
{
105
82.1k
    int type = ILI2_STRING_TYPE;
106
82.1k
    if (elem == nullptr)
107
0
        return type;
108
82.1k
    char *pszTagName = XMLString::transcode(elem->getTagName());
109
110
82.1k
    if (elem->getNodeType() == DOMNode::ELEMENT_NODE)
111
82.1k
    {
112
82.1k
        if (cmpStr(ILI2_COORD, pszTagName) == 0)
113
0
        {
114
0
            type = ILI2_COORD_TYPE;
115
0
        }
116
82.1k
        else if (cmpStr(ILI2_ARC, pszTagName) == 0)
117
0
        {
118
0
            type = ILI2_ARC_TYPE;
119
0
        }
120
82.1k
        else if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
121
0
        {
122
0
            type = ILI2_POLYLINE_TYPE;
123
0
        }
124
82.1k
        else if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
125
0
        {
126
0
            type = ILI2_BOUNDARY_TYPE;
127
0
        }
128
82.1k
        else if (cmpStr(ILI2_AREA, pszTagName) == 0)
129
0
        {
130
0
            type = ILI2_AREA_TYPE;
131
0
        }
132
82.1k
        else if (cmpStr(ILI2_SURFACE, pszTagName) == 0)
133
102
        {
134
102
            type = ILI2_AREA_TYPE;
135
102
        }
136
82.1k
    }
137
82.1k
    XMLString::release(&pszTagName);
138
82.1k
    return type;
139
82.1k
}
140
141
static char *getObjValue(DOMElement *elem)
142
25.3k
{
143
25.3k
    DOMNode *child = elem->getFirstChild();
144
25.3k
    if ((child != nullptr) && (child->getNodeType() == DOMNode::TEXT_NODE))
145
20.5k
    {
146
20.5k
        return CPLStrdup(transcode(child->getNodeValue()));
147
20.5k
    }
148
149
4.82k
    return nullptr;
150
25.3k
}
151
152
static char *getREFValue(DOMElement *elem)
153
4.82k
{
154
4.82k
    CPLString osREFValue(transcode(elem->getAttribute(ILI2_REF)));
155
4.82k
    return CPLStrdup(osREFValue);
156
4.82k
}
157
158
static OGRPoint *getPoint(DOMElement *elem)
159
2.68k
{
160
    // elem -> COORD (or ARC)
161
2.68k
    DOMElement *coordElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
162
2.68k
    if (coordElem == nullptr)
163
0
        return nullptr;
164
2.68k
    OGRPoint *pt = new OGRPoint();
165
166
8.06k
    while (coordElem != nullptr)
167
5.37k
    {
168
5.37k
        char *pszTagName = XMLString::transcode(coordElem->getTagName());
169
5.37k
        char *pszObjValue = getObjValue(coordElem);
170
5.37k
        if (pszObjValue)
171
5.37k
        {
172
5.37k
            if (cmpStr("C1", pszTagName) == 0)
173
2.68k
                pt->setX(CPLAtof(pszObjValue));
174
2.68k
            else if (cmpStr("C2", pszTagName) == 0)
175
2.68k
                pt->setY(CPLAtof(pszObjValue));
176
0
            else if (cmpStr("C3", pszTagName) == 0)
177
0
                pt->setZ(CPLAtof(pszObjValue));
178
5.37k
        }
179
5.37k
        CPLFree(pszObjValue);
180
5.37k
        XMLString::release(&pszTagName);
181
5.37k
        coordElem = dynamic_cast<DOMElement *>(coordElem->getNextSibling());
182
5.37k
    }
183
2.68k
    pt->flattenTo2D();
184
2.68k
    return pt;
185
2.68k
}
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
51
{
244
    // elem -> POLYLINE
245
51
    OGRCompoundCurve *ogrCurve = new OGRCompoundCurve();
246
51
    OGRLineString *ls = new OGRLineString();
247
248
51
    DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
249
2.73k
    while (lineElem != nullptr)
250
2.68k
    {
251
2.68k
        char *pszTagName = XMLString::transcode(lineElem->getTagName());
252
2.68k
        if (cmpStr(ILI2_COORD, pszTagName) == 0)
253
2.68k
        {
254
2.68k
            OGRPoint *poPoint = getPoint(lineElem);
255
2.68k
            if (poPoint)
256
2.68k
            {
257
2.68k
                ls->addPoint(poPoint);
258
2.68k
                delete poPoint;
259
2.68k
            }
260
2.68k
        }
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
2.68k
        XMLString::release(&pszTagName);
344
345
2.68k
        lineElem = dynamic_cast<DOMElement *>(lineElem->getNextSibling());
346
2.68k
    }
347
348
51
    if (ls->getNumPoints() > 1)
349
51
    {
350
51
        ogrCurve->addCurveDirectly(ls);
351
51
    }
352
0
    else
353
0
    {
354
0
        delete ls;
355
0
    }
356
51
    return ogrCurve;
357
51
}
358
359
static OGRCompoundCurve *getBoundary(DOMElement *elem)
360
51
{
361
362
51
    DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild());
363
51
    if (lineElem != nullptr)
364
51
    {
365
51
        char *pszTagName = XMLString::transcode(lineElem->getTagName());
366
51
        if (cmpStr(ILI2_POLYLINE, pszTagName) == 0)
367
51
        {
368
51
            XMLString::release(&pszTagName);
369
51
            return getPolyline(lineElem);
370
51
        }
371
0
        XMLString::release(&pszTagName);
372
0
    }
373
374
0
    return new OGRCompoundCurve();
375
51
}
376
377
static OGRCurvePolygon *getPolygon(DOMElement *elem)
378
50
{
379
50
    OGRCurvePolygon *pg = new OGRCurvePolygon();
380
381
50
    DOMElement *boundaryElem =
382
50
        dynamic_cast<DOMElement *>(elem->getFirstChild());  // outer boundary
383
101
    while (boundaryElem != nullptr)
384
51
    {
385
51
        char *pszTagName = XMLString::transcode(boundaryElem->getTagName());
386
51
        if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0)
387
51
        {
388
51
            OGRCompoundCurve *poCC = getBoundary(boundaryElem);
389
51
            if (pg->addRingDirectly(poCC) != OGRERR_NONE)
390
0
            {
391
0
                delete poCC;
392
0
            }
393
51
        }
394
51
        XMLString::release(&pszTagName);
395
51
        boundaryElem = dynamic_cast<DOMElement *>(
396
51
            boundaryElem->getNextSibling());  // inner boundaries
397
51
    }
398
399
50
    return pg;
400
50
}
401
402
OGRGeometry *ILI2Reader::getGeometry(DOMElement *elem, int type)
403
50
{
404
50
    OGRGeometryCollection *gm = new OGRGeometryCollection();
405
406
50
    DOMElement *childElem = elem;
407
50
    while (childElem != nullptr)
408
50
    {
409
50
        char *pszTagName = XMLString::transcode(childElem->getTagName());
410
50
        switch (type)
411
50
        {
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
50
            case ILI2_AREA_TYPE:
447
50
                if ((cmpStr(ILI2_AREA, pszTagName) == 0) ||
448
50
                    (cmpStr(ILI2_SURFACE, pszTagName) == 0))
449
50
                {
450
50
                    delete gm;
451
50
                    XMLString::release(&pszTagName);
452
50
                    return getPolygon(childElem);
453
50
                }
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
50
        }
465
0
        XMLString::release(&pszTagName);
466
467
        // GEOMCOLL
468
0
        childElem = dynamic_cast<DOMElement *>(childElem->getNextSibling());
469
0
    }
470
471
0
    return gm;
472
50
}
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
39.8k
{
491
39.8k
    DOMNode *node = elem;
492
39.8k
    if (getGeometryTypeOfElem(elem))
493
50
    {
494
50
        int depth = 0;  // Depth of value elem node
495
300
        for (node = elem; node; node = node->getParentNode())
496
250
            ++depth;
497
        // Field name is on level 4
498
50
        node = elem;
499
100
        for (int d = 0; d < depth - 4; ++d)
500
50
            node = node->getParentNode();
501
50
    }
502
39.8k
    if (node == nullptr)
503
0
    {
504
0
        CPLError(CE_Failure, CPLE_AssertionFailed, "node == NULL");
505
0
        return CPLStrdup("***bug***");
506
0
    }
507
39.8k
    return CPLStrdup(transcode(node->getNodeName()));
508
39.8k
}
509
510
void ILI2Reader::setFieldDefn(OGRFeatureDefn *featureDef, DOMElement *elem)
511
1.31k
{
512
1.31k
    int type = 0;
513
    // recursively search children
514
1.31k
    for (DOMNode *childNode = elem->getFirstChild();
515
3.02k
         type == 0 && childNode &&
516
3.02k
         childNode->getNodeType() == DOMNode::ELEMENT_NODE;
517
1.71k
         childNode = childNode->getNextSibling())
518
1.71k
    {
519
1.71k
        DOMElement *childElem = dynamic_cast<DOMElement *>(childNode);
520
1.71k
        CPLAssert(childElem);
521
1.71k
        type = getGeometryTypeOfElem(childElem);
522
1.71k
        if (type == 0)
523
1.71k
        {
524
1.71k
            if (childElem->getFirstChild() &&
525
1.71k
                childElem->getFirstChild()->getNodeType() ==
526
1.33k
                    DOMNode::ELEMENT_NODE)
527
490
            {
528
490
                setFieldDefn(featureDef, childElem);
529
490
            }
530
1.22k
            else
531
1.22k
            {
532
1.22k
                char *fName = fieldName(childElem);
533
1.22k
                if (featureDef->GetFieldIndex(fName) == -1)
534
1.14k
                {
535
1.14k
                    CPLDebug("OGR_ILI", "AddFieldDefn: %s", fName);
536
1.14k
                    OGRFieldDefn oFieldDefn(fName, OFTString);
537
1.14k
                    featureDef->AddFieldDefn(&oFieldDefn);
538
1.14k
                }
539
1.22k
                CPLFree(fName);
540
1.22k
            }
541
1.71k
        }
542
1.71k
    }
543
1.31k
}
544
545
void ILI2Reader::SetFieldValues(OGRFeature *feature, DOMElement *elem)
546
11.6k
{
547
11.6k
    int type = 0;
548
    // recursively search children
549
11.6k
    for (DOMNode *childNode = elem->getFirstChild();
550
52.2k
         type == 0 && childNode &&
551
52.2k
         childNode->getNodeType() == DOMNode::ELEMENT_NODE;
552
40.6k
         childNode = childNode->getNextSibling())
553
40.6k
    {
554
40.6k
        DOMElement *childElem = dynamic_cast<DOMElement *>(childNode);
555
40.6k
        CPLAssert(childElem);
556
40.6k
        type = getGeometryTypeOfElem(childElem);
557
40.6k
        if (type == 0)
558
40.5k
        {
559
40.5k
            if (childElem->getFirstChild() &&
560
40.5k
                childElem->getFirstChild()->getNodeType() ==
561
17.3k
                    DOMNode::ELEMENT_NODE)
562
2.01k
            {
563
2.01k
                SetFieldValues(feature, childElem);
564
2.01k
            }
565
38.5k
            else
566
38.5k
            {
567
38.5k
                char *fName = fieldName(childElem);
568
38.5k
                int fIndex = feature->GetFieldIndex(fName);
569
38.5k
                if (fIndex != -1)
570
20.0k
                {
571
20.0k
                    char *objVal = getObjValue(childElem);
572
20.0k
                    if (objVal == nullptr)
573
4.82k
                        objVal = getREFValue(childElem);  // only to try
574
20.0k
                    feature->SetField(fIndex, objVal);
575
20.0k
                    CPLFree(objVal);
576
20.0k
                }
577
18.5k
                else
578
18.5k
                {
579
18.5k
                    CPLDebug("OGR_ILI", "Attribute '%s' not found", fName);
580
18.5k
                    m_missAttrs.push_back(fName);
581
18.5k
                }
582
38.5k
                CPLFree(fName);
583
38.5k
            }
584
40.5k
        }
585
51
        else
586
51
        {
587
51
            char *fName = fieldName(childElem);
588
51
            int fIndex = feature->GetGeomFieldIndex(fName);
589
51
            OGRGeometry *geom = getGeometry(childElem, type);
590
51
            if (geom)
591
50
            {
592
50
                if (fIndex == -1)
593
50
                {  // Unknown model
594
50
                    feature->SetGeometryDirectly(geom);
595
50
                }
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
50
            }
613
51
            CPLFree(fName);
614
51
        }
615
40.6k
    }
616
11.6k
}
617
618
//
619
// ILI2Reader
620
//
621
IILI2Reader::~IILI2Reader()
622
395
{
623
395
}
624
625
ILI2Reader::ILI2Reader()
626
395
    : m_pszFilename(nullptr), m_poILI2Handler(nullptr), m_poSAXReader(nullptr),
627
395
      m_bReadStarted(FALSE), m_bXercesInitialized(false)
628
395
{
629
395
    SetupParser();
630
395
}
631
632
ILI2Reader::~ILI2Reader()
633
395
{
634
395
    CPLFree(m_pszFilename);
635
636
395
    CleanupParser();
637
638
395
    if (m_bXercesInitialized)
639
395
        OGRDeinitializeXerces();
640
395
}
641
642
void ILI2Reader::SetSourceFile(const char *pszFilename)
643
395
{
644
395
    CPLFree(m_pszFilename);
645
395
    m_pszFilename = CPLStrdup(pszFilename);
646
395
}
647
648
int ILI2Reader::SetupParser()
649
395
{
650
651
395
    if (!m_bXercesInitialized)
652
395
    {
653
395
        if (!OGRInitializeXerces())
654
0
            return FALSE;
655
395
        m_bXercesInitialized = true;
656
395
    }
657
658
    // Cleanup any old parser.
659
395
    if (m_poSAXReader != nullptr)
660
0
        CleanupParser();
661
662
    // Create and initialize parser.
663
395
    m_poSAXReader = XMLReaderFactory::createXMLReader();
664
665
395
    m_poILI2Handler = new ILI2Handler(this);
666
667
395
    m_poSAXReader->setContentHandler(m_poILI2Handler);
668
395
    m_poSAXReader->setErrorHandler(m_poILI2Handler);
669
395
    m_poSAXReader->setLexicalHandler(m_poILI2Handler);
670
395
    m_poSAXReader->setEntityResolver(m_poILI2Handler);
671
395
    m_poSAXReader->setDTDHandler(m_poILI2Handler);
672
395
    m_poSAXReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
673
395
                              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
395
    XMLCh *tmpCh =
691
395
        XMLString::transcode("http://xml.org/sax/features/validation");
692
395
    m_poSAXReader->setFeature(tmpCh, false);
693
395
    XMLString::release(&tmpCh);
694
395
    tmpCh = XMLString::transcode("http://xml.org/sax/features/namespaces");
695
395
    m_poSAXReader->setFeature(tmpCh, false);
696
395
    XMLString::release(&tmpCh);
697
    // #endif
698
699
395
    m_bReadStarted = FALSE;
700
701
395
    return TRUE;
702
395
}
703
704
void ILI2Reader::CleanupParser()
705
395
{
706
395
    if (m_poSAXReader == nullptr)
707
0
        return;
708
709
395
    delete m_poSAXReader;
710
395
    m_poSAXReader = nullptr;
711
712
395
    delete m_poILI2Handler;
713
395
    m_poILI2Handler = nullptr;
714
715
395
    m_bReadStarted = FALSE;
716
395
}
717
718
int ILI2Reader::SaveClasses(const char *pszFile = nullptr)
719
395
{
720
721
    // Add logic later to determine reasonable default schema file.
722
395
    if (pszFile == nullptr)
723
0
        return FALSE;
724
725
395
    VSILFILE *fp = VSIFOpenL(pszFile, "rb");
726
395
    if (fp == nullptr)
727
0
        return FALSE;
728
729
395
    InputSource *is = OGRCreateXercesInputSource(fp);
730
731
    // parse and create layers and features
732
395
    try
733
395
    {
734
395
        CPLDebug("OGR_ILI", "Parsing %s", pszFile);
735
395
        m_poSAXReader->parse(*is);
736
395
        VSIFCloseL(fp);
737
395
        OGRDestroyXercesInputSource(is);
738
395
    }
739
395
    catch (const DOMException &toCatch)
740
395
    {
741
        // Can happen with createElement() in ILI2Handler::startElement()
742
1
        CPLError(CE_Failure, CPLE_AppDefined, "DOMException: %s\n",
743
1
                 transcode(toCatch.getMessage()).c_str());
744
1
        VSIFCloseL(fp);
745
1
        OGRDestroyXercesInputSource(is);
746
1
        return FALSE;
747
1
    }
748
395
    catch (const SAXException &toCatch)
749
395
    {
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
394
    if (!m_missAttrs.empty())
758
121
    {
759
121
        m_missAttrs.sort();
760
121
        m_missAttrs.unique();
761
121
        string attrs = "";
762
121
        list<string>::const_iterator it;
763
2.25k
        for (it = m_missAttrs.begin(); it != m_missAttrs.end(); ++it)
764
2.13k
            attrs += *it + ", ";
765
766
121
        CPLError(CE_Warning, CPLE_NotSupported,
767
121
                 "Failed to add new definition to existing layers, attributes "
768
121
                 "not saved: %s",
769
121
                 attrs.c_str());
770
121
    }
771
772
394
    return TRUE;
773
395
}
774
775
std::vector<std::unique_ptr<OGRLayer>> &ILI2Reader::GetLayers()
776
980
{
777
980
    return m_listLayer;
778
980
}
779
780
int ILI2Reader::GetLayerCount()
781
577
{
782
577
    return static_cast<int>(m_listLayer.size());
783
577
}
784
785
OGRLayer *ILI2Reader::GetLayer(const char *pszName)
786
9.67k
{
787
92.2k
    for (auto it = m_listLayer.rbegin(); it != m_listLayer.rend(); ++it)
788
91.4k
    {
789
91.4k
        const OGRFeatureDefn *fDef = (*it)->GetLayerDefn();
790
91.4k
        if (cmpStr(fDef->GetName(), pszName) == 0)
791
8.85k
        {
792
8.85k
            return it->get();
793
8.85k
        }
794
91.4k
    }
795
823
    return nullptr;
796
9.67k
}
797
798
int ILI2Reader::AddFeature(DOMElement *elem)
799
9.67k
{
800
9.67k
    CPLString osName(transcode(elem->getTagName()));
801
    // CPLDebug( "OGR_ILI", "Reading layer: %s", osName.c_str() );
802
803
    // test if this layer exist
804
9.67k
    OGRILI2Layer *curLayer = cpl::down_cast<OGRILI2Layer *>(GetLayer(osName));
805
9.67k
    const bool needsNewLayer = (curLayer == nullptr);
806
9.67k
    std::unique_ptr<OGRILI2Layer> newLayer;
807
808
    // add a layer
809
9.67k
    if (needsNewLayer)
810
823
    {
811
823
        CPLDebug("OGR_ILI", "Adding layer: %s", osName.c_str());
812
823
        OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn(osName);
813
823
        poFeatureDefn->SetGeomType(wkbUnknown);
814
823
        GeomFieldInfos oGeomFieldInfos;
815
823
        newLayer = std::make_unique<OGRILI2Layer>(poFeatureDefn,
816
823
                                                  oGeomFieldInfos, nullptr);
817
823
        curLayer = newLayer.get();
818
823
    }
819
820
    // the feature and field definition
821
9.67k
    OGRFeatureDefn *featureDef = curLayer->GetLayerDefn();
822
9.67k
    if (needsNewLayer)
823
823
    {
824
        // add TID field
825
823
        OGRFieldDefn ofieldDefn(ILI2_TID, OFTString);
826
823
        featureDef->AddFieldDefn(&ofieldDefn);
827
828
823
        setFieldDefn(featureDef, elem);
829
823
    }
830
831
    // add the features
832
9.67k
    OGRFeature *feature = new OGRFeature(featureDef);
833
834
    // assign TID
835
9.67k
    int fIndex = feature->GetFieldIndex(ILI2_TID);
836
9.67k
    if (fIndex != -1)
837
9.67k
    {
838
9.67k
        feature->SetField(
839
9.67k
            fIndex, transcode(elem->getAttribute(xmlch_ILI2_TID)).c_str());
840
9.67k
    }
841
1
    else
842
1
    {
843
1
        CPLDebug("OGR_ILI", "'%s' not found", ILI2_TID);
844
1
    }
845
846
9.67k
    SetFieldValues(feature, elem);
847
9.67k
    curLayer->AddFeature(feature);
848
849
9.67k
    if (needsNewLayer)
850
822
        m_listLayer.push_back(std::move(newLayer));
851
852
9.67k
    return 0;
853
9.67k
}
854
855
IILI2Reader *CreateILI2Reader()
856
395
{
857
395
    return new ILI2Reader();
858
395
}
859
860
void DestroyILI2Reader(IILI2Reader *reader)
861
9.84k
{
862
9.84k
    if (reader)
863
395
        delete reader;
864
9.84k
}