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/ntf/ntf_generic.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  NTF Translator
4
 * Purpose:  Handle NTF products that aren't recognised generically.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include <stdarg.h>
14
#include "ntf.h"
15
#include "cpl_string.h"
16
17
/************************************************************************/
18
/* ==================================================================== */
19
/*                          NTFGenericClass                             */
20
/*                                                                      */
21
/*      The NTFGenericClass class exists to hold aggregated             */
22
/*      information for each type of record encountered in a set of     */
23
/*      NTF files, primarily the list of attributes actually            */
24
/*      encountered.                                                    */
25
/* ==================================================================== */
26
/************************************************************************/
27
28
/************************************************************************/
29
/*                           NTFGenericClass                            */
30
/************************************************************************/
31
32
NTFGenericClass::NTFGenericClass()
33
1.28M
    : nFeatureCount(0), b3D(FALSE), nAttrCount(0), papszAttrNames(nullptr),
34
1.28M
      papszAttrFormats(nullptr), panAttrMaxWidth(nullptr),
35
1.28M
      pabAttrMultiple(nullptr)
36
1.28M
{
37
1.28M
}
38
39
/************************************************************************/
40
/*                           ~NTFGenericClass                           */
41
/************************************************************************/
42
43
NTFGenericClass::~NTFGenericClass()
44
45
1.28M
{
46
1.28M
    CSLDestroy(papszAttrNames);
47
1.28M
    CSLDestroy(papszAttrFormats);
48
1.28M
    CPLFree(panAttrMaxWidth);
49
1.28M
    CPLFree(pabAttrMultiple);
50
1.28M
}
51
52
/************************************************************************/
53
/*                            CheckAddAttr()                            */
54
/*                                                                      */
55
/*      Check if an attribute already exists.  If not add it with       */
56
/*      its format.  Note we don't check for format conflicts at        */
57
/*      this time.                                                      */
58
/************************************************************************/
59
60
void NTFGenericClass::CheckAddAttr(const char *pszName, const char *pszFormat,
61
                                   int nWidth)
62
63
0
{
64
0
    if (EQUAL(pszName, "TX"))
65
0
        pszName = "TEXT";
66
0
    if (EQUAL(pszName, "FC"))
67
0
        pszName = "FEAT_CODE";
68
69
0
    const int iAttrOffset = CSLFindString(papszAttrNames, pszName);
70
71
0
    if (iAttrOffset == -1)
72
0
    {
73
0
        nAttrCount++;
74
75
0
        papszAttrNames = CSLAddString(papszAttrNames, pszName);
76
0
        papszAttrFormats = CSLAddString(papszAttrFormats, pszFormat);
77
78
0
        panAttrMaxWidth = static_cast<int *>(
79
0
            CPLRealloc(panAttrMaxWidth, sizeof(int) * nAttrCount));
80
81
0
        panAttrMaxWidth[nAttrCount - 1] = nWidth;
82
83
0
        pabAttrMultiple = static_cast<int *>(
84
0
            CPLRealloc(pabAttrMultiple, sizeof(int) * nAttrCount));
85
86
0
        pabAttrMultiple[nAttrCount - 1] = FALSE;
87
0
    }
88
0
    else
89
0
    {
90
0
        if (panAttrMaxWidth[iAttrOffset] < nWidth)
91
0
            panAttrMaxWidth[iAttrOffset] = nWidth;
92
0
    }
93
0
}
94
95
/************************************************************************/
96
/*                            SetMultiple()                             */
97
/*                                                                      */
98
/*      Mark this attribute as appearing multiple times on some         */
99
/*      features.                                                       */
100
/************************************************************************/
101
102
void NTFGenericClass::SetMultiple(const char *pszName)
103
104
0
{
105
0
    if (EQUAL(pszName, "TX"))
106
0
        pszName = "TEXT";
107
0
    if (EQUAL(pszName, "FC"))
108
0
        pszName = "FEAT_CODE";
109
110
0
    const int iAttrOffset = CSLFindString(papszAttrNames, pszName);
111
0
    if (iAttrOffset == -1)
112
0
        return;
113
114
0
    pabAttrMultiple[iAttrOffset] = TRUE;
115
0
}
116
117
/************************************************************************/
118
/*                           WorkupGeneric()                            */
119
/*                                                                      */
120
/*      Scan a whole file, in order to build up a list of attributes    */
121
/*      for the generic types.                                          */
122
/************************************************************************/
123
124
void OGRNTFDataSource::WorkupGeneric(NTFFileReader *poReader)
125
126
0
{
127
0
    NTFRecord **papoGroup = nullptr;
128
129
0
    if (poReader->GetNTFLevel() > 2)
130
0
    {
131
0
        poReader->IndexFile();
132
0
        if (CPLGetLastErrorType() == CE_Failure)
133
0
            return;
134
0
    }
135
0
    else
136
0
        poReader->Reset();
137
138
    /* ==================================================================== */
139
    /*      Read all record groups in the file.                             */
140
    /* ==================================================================== */
141
0
    while (true)
142
0
    {
143
        /* --------------------------------------------------------------------
144
         */
145
        /*      Read a record group */
146
        /* --------------------------------------------------------------------
147
         */
148
0
        if (poReader->GetNTFLevel() > 2)
149
0
            papoGroup = poReader->GetNextIndexedRecordGroup(papoGroup);
150
0
        else
151
0
            papoGroup = poReader->ReadRecordGroup();
152
153
0
        if (papoGroup == nullptr || papoGroup[0]->GetType() < 0 ||
154
0
            papoGroup[0]->GetType() >= 99)
155
0
            break;
156
157
        /* --------------------------------------------------------------------
158
         */
159
        /*      Get the class corresponding to the anchor record. */
160
        /* --------------------------------------------------------------------
161
         */
162
0
        NTFGenericClass *poClass = GetGClass(papoGroup[0]->GetType());
163
0
        char **papszFullAttList = nullptr;
164
165
0
        poClass->nFeatureCount++;
166
167
        /* --------------------------------------------------------------------
168
         */
169
        /*      Loop over constituent records collecting attributes. */
170
        /* --------------------------------------------------------------------
171
         */
172
0
        for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
173
0
        {
174
0
            NTFRecord *poRecord = papoGroup[iRec];
175
176
0
            switch (poRecord->GetType())
177
0
            {
178
0
                case NRT_ATTREC:
179
0
                {
180
0
                    char **papszTypes, **papszValues;
181
182
0
                    poReader->ProcessAttRec(poRecord, nullptr, &papszTypes,
183
0
                                            &papszValues);
184
185
0
                    for (int iAtt = 0;
186
0
                         papszTypes != nullptr && papszTypes[iAtt] != nullptr;
187
0
                         iAtt++)
188
0
                    {
189
0
                        NTFAttDesc *poAttDesc =
190
0
                            poReader->GetAttDesc(papszTypes[iAtt]);
191
0
                        if (poAttDesc != nullptr &&
192
0
                            papszValues[iAtt] != nullptr)
193
0
                        {
194
0
                            poClass->CheckAddAttr(
195
0
                                poAttDesc->val_type, poAttDesc->finter,
196
0
                                static_cast<int>(strlen(papszValues[iAtt])));
197
0
                        }
198
199
0
                        if (CSLFindString(papszFullAttList, papszTypes[iAtt]) ==
200
0
                            -1)
201
0
                            papszFullAttList = CSLAddString(papszFullAttList,
202
0
                                                            papszTypes[iAtt]);
203
0
                        else if (poAttDesc != nullptr)
204
0
                            poClass->SetMultiple(poAttDesc->val_type);
205
0
                    }
206
207
0
                    CSLDestroy(papszTypes);
208
0
                    CSLDestroy(papszValues);
209
0
                }
210
0
                break;
211
212
0
                case NRT_TEXTREP:
213
0
                case NRT_NAMEPOSTN:
214
0
                    poClass->CheckAddAttr("FONT", "I4", 4);
215
0
                    poClass->CheckAddAttr("TEXT_HT", "R3,1", 3);
216
0
                    poClass->CheckAddAttr("TEXT_HT_GROUND", "R9,3", 9);
217
0
                    poClass->CheckAddAttr("TEXT_HT", "R3,1", 3);
218
0
                    poClass->CheckAddAttr("DIG_POSTN", "I1", 1);
219
0
                    poClass->CheckAddAttr("ORIENT", "R4,1", 4);
220
0
                    break;
221
222
0
                case NRT_NAMEREC:
223
0
                    poClass->CheckAddAttr("TEXT", "A*",
224
0
                                          atoi(poRecord->GetField(13, 14)));
225
0
                    break;
226
227
0
                case NRT_GEOMETRY:
228
0
                case NRT_GEOMETRY3D:
229
0
                    if (atoi(poRecord->GetField(3, 8)) != 0)
230
0
                        poClass->CheckAddAttr("GEOM_ID", "I6", 6);
231
0
                    if (poRecord->GetType() == NRT_GEOMETRY3D)
232
0
                        poClass->b3D = TRUE;
233
0
                    break;
234
235
0
                case NRT_POINTREC:
236
0
                case NRT_LINEREC:
237
0
                    if (poReader->GetNTFLevel() < 3)
238
0
                    {
239
0
                        NTFAttDesc *poAttDesc =
240
0
                            poReader->GetAttDesc(poRecord->GetField(9, 10));
241
0
                        if (poAttDesc != nullptr)
242
0
                            poClass->CheckAddAttr(poAttDesc->val_type,
243
0
                                                  poAttDesc->finter, 6);
244
245
0
                        if (!EQUAL(poRecord->GetField(17, 20), "    "))
246
0
                            poClass->CheckAddAttr("FEAT_CODE", "A4", 4);
247
0
                    }
248
0
                    break;
249
250
0
                default:
251
0
                    break;
252
0
            }
253
0
        }
254
255
0
        CSLDestroy(papszFullAttList);
256
0
    }
257
258
0
    if (GetOption("CACHING") != nullptr && EQUAL(GetOption("CACHING"), "OFF"))
259
0
        poReader->DestroyIndex();
260
261
0
    poReader->Reset();
262
0
}
263
264
/************************************************************************/
265
/*                        AddGenericAttributes()                        */
266
/************************************************************************/
267
268
static void AddGenericAttributes(NTFFileReader *poReader, NTFRecord **papoGroup,
269
                                 OGRFeature *poFeature)
270
271
0
{
272
0
    char **papszTypes = nullptr;
273
0
    char **papszValues = nullptr;
274
275
0
    if (!poReader->ProcessAttRecGroup(papoGroup, &papszTypes, &papszValues))
276
0
        return;
277
278
0
    for (int iAtt = 0; papszTypes != nullptr && papszTypes[iAtt] != nullptr;
279
0
         iAtt++)
280
0
    {
281
0
        int iField = 0;
282
283
0
        if (EQUAL(papszTypes[iAtt], "TX"))
284
0
            iField = poFeature->GetFieldIndex("TEXT");
285
0
        else if (EQUAL(papszTypes[iAtt], "FC"))
286
0
            iField = poFeature->GetFieldIndex("FEAT_CODE");
287
0
        else
288
0
            iField = poFeature->GetFieldIndex(papszTypes[iAtt]);
289
290
0
        if (iField == -1)
291
0
            continue;
292
293
0
        poReader->ApplyAttributeValue(poFeature, iField, papszTypes[iAtt],
294
0
                                      papszTypes, papszValues);
295
296
        /* --------------------------------------------------------------------
297
         */
298
        /*      Do we have a corresponding list field we should be */
299
        /*      accumulating this into? */
300
        /* --------------------------------------------------------------------
301
         */
302
0
        char szListName[128] = {};
303
304
0
        snprintf(szListName, sizeof(szListName), "%s_LIST",
305
0
                 poFeature->GetFieldDefnRef(iField)->GetNameRef());
306
0
        const int iListField = poFeature->GetFieldIndex(szListName);
307
308
        /* --------------------------------------------------------------------
309
         */
310
        /*      Yes, so perform processing similar to ApplyAttributeValue(), */
311
        /*      and append to list value. */
312
        /* --------------------------------------------------------------------
313
         */
314
0
        if (iListField != -1)
315
0
        {
316
0
            const char *pszAttLongName = nullptr;
317
0
            const char *pszAttValue = nullptr;
318
0
            const char *pszCodeDesc = nullptr;
319
320
0
            poReader->ProcessAttValue(papszTypes[iAtt], papszValues[iAtt],
321
0
                                      &pszAttLongName, &pszAttValue,
322
0
                                      &pszCodeDesc);
323
324
0
            if (poFeature->IsFieldSetAndNotNull(iListField))
325
0
            {
326
0
                poFeature->SetField(
327
0
                    iListField,
328
0
                    CPLSPrintf("%s,%s", poFeature->GetFieldAsString(iListField),
329
0
                               pszAttValue));
330
0
            }
331
0
            else
332
0
            {
333
0
                poFeature->SetField(iListField, pszAttValue);
334
0
            }
335
0
        }
336
0
    }
337
338
0
    CSLDestroy(papszTypes);
339
0
    CSLDestroy(papszValues);
340
0
}
341
342
/************************************************************************/
343
/*                        TranslateGenericNode()                        */
344
/************************************************************************/
345
346
static OGRFeature *TranslateGenericNode(NTFFileReader *poReader,
347
                                        OGRNTFLayer *poLayer,
348
                                        NTFRecord **papoGroup)
349
350
0
{
351
0
    if (CSLCount((char **)papoGroup) < 2 ||
352
0
        papoGroup[0]->GetType() != NRT_NODEREC ||
353
0
        (papoGroup[1]->GetType() != NRT_GEOMETRY &&
354
0
         papoGroup[1]->GetType() != NRT_GEOMETRY3D))
355
0
    {
356
0
        return nullptr;
357
0
    }
358
359
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
360
361
    // NODE_ID
362
0
    poFeature->SetField("NODE_ID", atoi(papoGroup[0]->GetField(3, 8)));
363
364
    // Geometry
365
0
    poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
366
0
    poFeature->SetField("GEOM_ID", papoGroup[1]->GetField(3, 8));
367
368
    // NUM_LINKS
369
0
    int nLinkCount = 0;
370
0
    if (papoGroup[0]->GetLength() > 18)
371
0
    {
372
0
        nLinkCount = atoi(papoGroup[0]->GetField(15, 18));
373
0
        if (nLinkCount > 0)
374
0
        {
375
0
            std::vector<int> anLinks(nLinkCount);
376
377
            // GEOM_ID_OF_LINK
378
0
            for (int iLink = 0; iLink < nLinkCount; iLink++)
379
0
                anLinks[iLink] = atoi(
380
0
                    papoGroup[0]->GetField(20 + iLink * 12, 25 + iLink * 12));
381
382
0
            poFeature->SetField("GEOM_ID_OF_LINK", nLinkCount, anLinks.data());
383
384
            // DIR
385
0
            for (int iLink = 0; iLink < nLinkCount; iLink++)
386
0
                anLinks[iLink] = atoi(
387
0
                    papoGroup[0]->GetField(19 + iLink * 12, 19 + iLink * 12));
388
389
0
            poFeature->SetField("DIR", nLinkCount, anLinks.data());
390
0
        }
391
0
    }
392
393
0
    poFeature->SetField("NUM_LINKS", nLinkCount);
394
395
    // should we add LEVEL and/or ORIENT?
396
397
0
    return poFeature;
398
0
}
399
400
/************************************************************************/
401
/*                     TranslateGenericCollection()                     */
402
/************************************************************************/
403
404
static OGRFeature *TranslateGenericCollection(NTFFileReader *poReader,
405
                                              OGRNTFLayer *poLayer,
406
                                              NTFRecord **papoGroup)
407
408
0
{
409
0
    if (CSLCount((char **)papoGroup) < 1 ||
410
0
        papoGroup[0]->GetType() != NRT_COLLECT)
411
0
        return nullptr;
412
413
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
414
415
    // COLL_ID
416
0
    poFeature->SetField("COLL_ID", atoi(papoGroup[0]->GetField(3, 8)));
417
418
    // NUM_PARTS
419
0
    int nPartCount = 0;
420
421
0
    if (papoGroup[0]->GetLength() >= 20)
422
0
    {
423
0
        nPartCount = atoi(papoGroup[0]->GetField(9, 12));
424
0
        if (nPartCount > 0 &&
425
0
            nPartCount - 1 <= (papoGroup[0]->GetLength() - 20) / 8)
426
0
        {
427
0
            std::vector<int> anParts(nPartCount);
428
429
            // TYPE
430
0
            for (int iPart = 0; iPart < nPartCount; iPart++)
431
0
                anParts[iPart] = atoi(
432
0
                    papoGroup[0]->GetField(13 + iPart * 8, 14 + iPart * 8));
433
434
0
            poFeature->SetField("TYPE", nPartCount, anParts.data());
435
436
            // ID
437
0
            for (int iPart = 0; iPart < nPartCount; iPart++)
438
0
                anParts[iPart] = atoi(
439
0
                    papoGroup[0]->GetField(15 + iPart * 8, 20 + iPart * 8));
440
441
0
            poFeature->SetField("ID", nPartCount, anParts.data());
442
0
        }
443
0
        else
444
0
        {
445
0
            nPartCount = 0;
446
0
        }
447
0
    }
448
449
0
    poFeature->SetField("NUM_PARTS", nPartCount);
450
451
    // ATTREC Attributes
452
0
    AddGenericAttributes(poReader, papoGroup, poFeature);
453
454
0
    return poFeature;
455
0
}
456
457
/************************************************************************/
458
/*                        TranslateGenericText()                        */
459
/************************************************************************/
460
461
static OGRFeature *TranslateGenericText(NTFFileReader *poReader,
462
                                        OGRNTFLayer *poLayer,
463
                                        NTFRecord **papoGroup)
464
465
0
{
466
0
    if (CSLCount((char **)papoGroup) < 2 ||
467
0
        papoGroup[0]->GetType() != NRT_TEXTREC)
468
0
        return nullptr;
469
470
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
471
472
    // TEXT_ID
473
0
    poFeature->SetField("TEXT_ID", atoi(papoGroup[0]->GetField(3, 8)));
474
475
    // Geometry
476
0
    for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
477
0
    {
478
0
        if (papoGroup[iRec]->GetType() == NRT_GEOMETRY ||
479
0
            papoGroup[iRec]->GetType() == NRT_GEOMETRY3D)
480
0
        {
481
0
            poFeature->SetGeometryDirectly(
482
0
                poReader->ProcessGeometry(papoGroup[iRec]));
483
0
            poFeature->SetField("GEOM_ID", papoGroup[iRec]->GetField(3, 8));
484
0
            break;
485
0
        }
486
0
    }
487
488
    // ATTREC Attributes
489
0
    AddGenericAttributes(poReader, papoGroup, poFeature);
490
491
    // TEXTREP information
492
0
    for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
493
0
    {
494
0
        NTFRecord *poRecord = papoGroup[iRec];
495
496
0
        if (poRecord->GetType() == NRT_TEXTREP)
497
0
        {
498
0
            poFeature->SetField("FONT", atoi(poRecord->GetField(9, 12)));
499
0
            poFeature->SetField("TEXT_HT",
500
0
                                atoi(poRecord->GetField(13, 15)) * 0.1);
501
0
            poFeature->SetField("TEXT_HT_GROUND",
502
0
                                atoi(poRecord->GetField(13, 15)) * 0.1 *
503
0
                                    poReader->GetPaperToGround());
504
0
            poFeature->SetField("DIG_POSTN", atoi(poRecord->GetField(16, 16)));
505
0
            poFeature->SetField("ORIENT",
506
0
                                atoi(poRecord->GetField(17, 20)) * 0.1);
507
0
            break;
508
0
        }
509
0
    }
510
511
0
    return poFeature;
512
0
}
513
514
/************************************************************************/
515
/*                        TranslateGenericName()                        */
516
/************************************************************************/
517
518
static OGRFeature *TranslateGenericName(NTFFileReader *poReader,
519
                                        OGRNTFLayer *poLayer,
520
                                        NTFRecord **papoGroup)
521
522
0
{
523
0
    if (CSLCount((char **)papoGroup) < 2 ||
524
0
        papoGroup[0]->GetType() != NRT_NAMEREC)
525
0
        return nullptr;
526
527
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
528
529
    // NAME_ID
530
0
    poFeature->SetField("NAME_ID", atoi(papoGroup[0]->GetField(3, 8)));
531
532
    // TEXT_CODE
533
0
    poFeature->SetField("TEXT_CODE", papoGroup[0]->GetField(8, 12));
534
535
    // TEXT
536
0
    int nNumChar = atoi(papoGroup[0]->GetField(13, 14));
537
538
0
    if (nNumChar > 0 && papoGroup[0]->GetLength() >= 15 + nNumChar - 1)
539
0
        poFeature->SetField("TEXT",
540
0
                            papoGroup[0]->GetField(15, 15 + nNumChar - 1));
541
542
    // Geometry
543
0
    for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
544
0
    {
545
0
        if (papoGroup[iRec]->GetType() == NRT_GEOMETRY ||
546
0
            papoGroup[iRec]->GetType() == NRT_GEOMETRY3D)
547
0
        {
548
0
            poFeature->SetGeometryDirectly(
549
0
                poReader->ProcessGeometry(papoGroup[iRec]));
550
0
            poFeature->SetField("GEOM_ID", papoGroup[iRec]->GetField(3, 8));
551
0
            break;
552
0
        }
553
0
    }
554
555
    // ATTREC Attributes
556
0
    AddGenericAttributes(poReader, papoGroup, poFeature);
557
558
    // NAMEPOSTN information
559
0
    for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
560
0
    {
561
0
        NTFRecord *poRecord = papoGroup[iRec];
562
563
0
        if (poRecord->GetType() == NRT_NAMEPOSTN)
564
0
        {
565
0
            poFeature->SetField("FONT", atoi(poRecord->GetField(3, 6)));
566
0
            poFeature->SetField("TEXT_HT",
567
0
                                atoi(poRecord->GetField(7, 9)) * 0.1);
568
0
            poFeature->SetField("TEXT_HT_GROUND",
569
0
                                atoi(poRecord->GetField(7, 9)) * 0.1 *
570
0
                                    poReader->GetPaperToGround());
571
0
            poFeature->SetField("DIG_POSTN", atoi(poRecord->GetField(10, 10)));
572
0
            poFeature->SetField("ORIENT",
573
0
                                atoi(poRecord->GetField(11, 14)) * 0.1);
574
0
            break;
575
0
        }
576
0
    }
577
578
0
    return poFeature;
579
0
}
580
581
/************************************************************************/
582
/*                       TranslateGenericPoint()                        */
583
/************************************************************************/
584
585
static OGRFeature *TranslateGenericPoint(NTFFileReader *poReader,
586
                                         OGRNTFLayer *poLayer,
587
                                         NTFRecord **papoGroup)
588
589
0
{
590
0
    if (CSLCount((char **)papoGroup) < 2 ||
591
0
        papoGroup[0]->GetType() != NRT_POINTREC ||
592
0
        (papoGroup[1]->GetType() != NRT_GEOMETRY &&
593
0
         papoGroup[1]->GetType() != NRT_GEOMETRY3D))
594
0
    {
595
0
        return nullptr;
596
0
    }
597
598
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
599
600
    // POINT_ID
601
0
    poFeature->SetField("POINT_ID", atoi(papoGroup[0]->GetField(3, 8)));
602
603
    // Geometry
604
0
    poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
605
0
    poFeature->SetField("GEOM_ID", papoGroup[1]->GetField(3, 8));
606
607
    // ATTREC Attributes
608
0
    AddGenericAttributes(poReader, papoGroup, poFeature);
609
610
    // Handle singular attribute in pre-level 3 POINTREC.
611
0
    if (poReader->GetNTFLevel() < 3)
612
0
    {
613
0
        char szValType[3];
614
615
0
        snprintf(szValType, sizeof(szValType), "%s",
616
0
                 papoGroup[0]->GetField(9, 10));
617
0
        if (!EQUAL(szValType, "  "))
618
0
        {
619
0
            const char *pszProcessedValue = nullptr;
620
621
0
            if (poReader->ProcessAttValue(szValType,
622
0
                                          papoGroup[0]->GetField(11, 16),
623
0
                                          nullptr, &pszProcessedValue, nullptr))
624
0
                poFeature->SetField(szValType, pszProcessedValue);
625
0
        }
626
627
0
        if (!EQUAL(papoGroup[0]->GetField(17, 20), "    "))
628
0
        {
629
0
            poFeature->SetField("FEAT_CODE", papoGroup[0]->GetField(17, 20));
630
0
        }
631
0
    }
632
633
0
    return poFeature;
634
0
}
635
636
/************************************************************************/
637
/*                        TranslateGenericLine()                        */
638
/************************************************************************/
639
640
static OGRFeature *TranslateGenericLine(NTFFileReader *poReader,
641
                                        OGRNTFLayer *poLayer,
642
                                        NTFRecord **papoGroup)
643
644
0
{
645
0
    if (CSLCount((char **)papoGroup) < 2 ||
646
0
        papoGroup[0]->GetType() != NRT_LINEREC ||
647
0
        (papoGroup[1]->GetType() != NRT_GEOMETRY &&
648
0
         papoGroup[1]->GetType() != NRT_GEOMETRY3D))
649
0
        return nullptr;
650
651
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
652
653
    // LINE_ID
654
0
    poFeature->SetField("LINE_ID", atoi(papoGroup[0]->GetField(3, 8)));
655
656
    // Geometry
657
0
    poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
658
0
    poFeature->SetField("GEOM_ID", papoGroup[1]->GetField(3, 8));
659
660
    // ATTREC Attributes
661
0
    AddGenericAttributes(poReader, papoGroup, poFeature);
662
663
    // Handle singular attribute in pre-level 3 LINEREC.
664
0
    if (poReader->GetNTFLevel() < 3)
665
0
    {
666
0
        char szValType[3] = {};
667
668
0
        snprintf(szValType, sizeof(szValType), "%s",
669
0
                 papoGroup[0]->GetField(9, 10));
670
0
        if (!EQUAL(szValType, "  "))
671
0
        {
672
0
            const char *pszProcessedValue = nullptr;
673
674
0
            if (poReader->ProcessAttValue(szValType,
675
0
                                          papoGroup[0]->GetField(11, 16),
676
0
                                          nullptr, &pszProcessedValue, nullptr))
677
0
                poFeature->SetField(szValType, pszProcessedValue);
678
0
        }
679
680
0
        if (!EQUAL(papoGroup[0]->GetField(17, 20), "    "))
681
0
        {
682
0
            poFeature->SetField("FEAT_CODE", papoGroup[0]->GetField(17, 20));
683
0
        }
684
0
    }
685
686
0
    return poFeature;
687
0
}
688
689
/************************************************************************/
690
/*                        TranslateGenericPoly()                        */
691
/************************************************************************/
692
693
static OGRFeature *TranslateGenericPoly(NTFFileReader *poReader,
694
                                        OGRNTFLayer *poLayer,
695
                                        NTFRecord **papoGroup)
696
697
0
{
698
    /* ==================================================================== */
699
    /*      Traditional POLYGON record groups.                              */
700
    /* ==================================================================== */
701
0
    if (CSLCount((char **)papoGroup) >= 2 &&
702
0
        papoGroup[0]->GetType() == NRT_POLYGON &&
703
0
        papoGroup[1]->GetType() == NRT_CHAIN)
704
0
    {
705
0
        OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
706
707
        // POLY_ID
708
0
        poFeature->SetField(0, atoi(papoGroup[0]->GetField(3, 8)));
709
710
        // NUM_PARTS
711
0
        int nNumLinks = atoi(papoGroup[1]->GetField(9, 12));
712
713
0
        if (nNumLinks < 0 || nNumLinks > MAX_LINK)
714
0
        {
715
0
            CPLError(CE_Failure, CPLE_AppDefined,
716
0
                     "MAX_LINK exceeded in ntf_generic.cpp.");
717
0
            return poFeature;
718
0
        }
719
720
0
        poFeature->SetField("NUM_PARTS", nNumLinks);
721
722
        // DIR
723
0
        int i, anList[MAX_LINK] = {0};
724
725
0
        for (i = 0; i < nNumLinks; i++)
726
0
            anList[i] = atoi(papoGroup[1]->GetField(19 + i * 7, 19 + i * 7));
727
728
0
        poFeature->SetField("DIR", nNumLinks, anList);
729
730
        // GEOM_ID_OF_LINK
731
0
        for (i = 0; i < nNumLinks; i++)
732
0
            anList[i] = atoi(papoGroup[1]->GetField(13 + i * 7, 18 + i * 7));
733
734
0
        poFeature->SetField("GEOM_ID_OF_LINK", nNumLinks, anList);
735
736
        // RingStart
737
0
        int nRingList = 0;
738
0
        poFeature->SetField("RingStart", 1, &nRingList);
739
740
        // ATTREC Attributes
741
0
        AddGenericAttributes(poReader, papoGroup, poFeature);
742
743
        // Read point geometry
744
0
        if (papoGroup[2] != nullptr &&
745
0
            (papoGroup[2]->GetType() == NRT_GEOMETRY ||
746
0
             papoGroup[2]->GetType() == NRT_GEOMETRY3D))
747
0
        {
748
0
            poFeature->SetGeometryDirectly(
749
0
                poReader->ProcessGeometry(papoGroup[2]));
750
0
            poFeature->SetField("GEOM_ID", papoGroup[2]->GetField(3, 8));
751
0
        }
752
753
0
        return poFeature;
754
0
    }
755
756
0
    return nullptr;
757
0
}
758
759
/************************************************************************/
760
/*                       TranslateGenericCPoly()                        */
761
/************************************************************************/
762
763
static OGRFeature *TranslateGenericCPoly(NTFFileReader *poReader,
764
                                         OGRNTFLayer *poLayer,
765
                                         NTFRecord **papoGroup)
766
767
0
{
768
    /* -------------------------------------------------------------------- */
769
    /*      First we do validation of the grouping.                         */
770
    /* -------------------------------------------------------------------- */
771
0
    if (papoGroup[0]->GetType() != NRT_CPOLY)
772
0
        return nullptr;
773
774
0
    if (papoGroup[1] == nullptr || (papoGroup[1]->GetType() != NRT_GEOMETRY &&
775
0
                                    papoGroup[1]->GetType() != NRT_GEOMETRY3D))
776
0
        return nullptr;
777
778
0
    if (papoGroup[2] != nullptr && papoGroup[2]->GetType() != NRT_ATTREC)
779
0
        return nullptr;
780
781
    /* -------------------------------------------------------------------- */
782
    /*      collect information for whole complex polygon.                  */
783
    /* -------------------------------------------------------------------- */
784
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
785
786
    // CPOLY_ID
787
0
    poFeature->SetField("CPOLY_ID", atoi(papoGroup[0]->GetField(3, 8)));
788
789
    // ATTREC Attributes
790
0
    AddGenericAttributes(poReader, papoGroup, poFeature);
791
792
    // Read point geometry
793
0
    if (papoGroup[1] != nullptr && (papoGroup[1]->GetType() == NRT_GEOMETRY ||
794
0
                                    papoGroup[1]->GetType() == NRT_GEOMETRY3D))
795
0
    {
796
0
        poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
797
0
        poFeature->SetField("GEOM_ID", atoi(papoGroup[1]->GetField(3, 8)));
798
0
    }
799
800
    /* -------------------------------------------------------------------- */
801
    /*      Collect the chains for each of the rings, and just aggregate    */
802
    /*      these into the master list without any concept of where the     */
803
    /*      boundaries are.  The boundary information will be emitted      */
804
    /*      in the RingStart field.                                         */
805
    /* -------------------------------------------------------------------- */
806
0
    int nNumLink = 0;
807
0
    int anPolyId[MAX_LINK * 2] = {0};
808
809
0
    nNumLink = atoi(papoGroup[0]->GetField(9, 12));
810
0
    if (nNumLink < 0 || nNumLink > MAX_LINK)
811
0
    {
812
0
        CPLError(CE_Failure, CPLE_AppDefined,
813
0
                 "MAX_LINK exceeded in ntf_generic.cpp.");
814
0
        return poFeature;
815
0
    }
816
817
0
    for (int iLink = 0; iLink < nNumLink; iLink++)
818
0
    {
819
0
        anPolyId[iLink] =
820
0
            atoi(papoGroup[0]->GetField(13 + iLink * 7, 18 + iLink * 7));
821
0
    }
822
823
    // NUM_PARTS
824
0
    poFeature->SetField("NUM_PARTS", nNumLink);
825
826
    // POLY_ID
827
0
    poFeature->SetField("POLY_ID", nNumLink, anPolyId);
828
829
0
    return poFeature;
830
0
}
831
832
/************************************************************************/
833
/*                       EstablishGenericLayers()                       */
834
/************************************************************************/
835
836
void OGRNTFDataSource::EstablishGenericLayers()
837
838
0
{
839
    /* -------------------------------------------------------------------- */
840
    /*      Pick an initial NTFFileReader to build the layers against.      */
841
    /* -------------------------------------------------------------------- */
842
0
    for (int iFile = 0; iFile < nNTFFileCount; iFile++)
843
0
    {
844
0
        int bHasZ = FALSE;
845
846
0
        NTFFileReader *poPReader = papoNTFFileReader[iFile];
847
0
        if (poPReader->GetProductId() != NPC_UNKNOWN)
848
0
            continue;
849
850
        /* --------------------------------------------------------------------
851
         */
852
        /*      If any of the generic classes are 3D, then assume all our */
853
        /*      geometry should be marked as 3D. */
854
        /* --------------------------------------------------------------------
855
         */
856
0
        for (int iType = 0; iType < 99; iType++)
857
0
        {
858
0
            NTFGenericClass *poClass = aoGenericClass + iType;
859
860
0
            if (poClass->nFeatureCount > 0 && poClass->b3D)
861
0
                bHasZ = TRUE;
862
0
        }
863
864
        /* --------------------------------------------------------------------
865
         */
866
        /*      Create layers for all recognised layer types with features. */
867
        /* --------------------------------------------------------------------
868
         */
869
0
        for (int iType = 0; iType < 99; iType++)
870
0
        {
871
0
            NTFGenericClass *poClass = aoGenericClass + iType;
872
873
0
            if (poClass->nFeatureCount == 0)
874
0
                continue;
875
876
0
            if (iType == NRT_POINTREC)
877
0
            {
878
0
                poPReader->EstablishLayer(
879
0
                    "GENERIC_POINT", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
880
0
                    TranslateGenericPoint, NRT_POINTREC, poClass, "POINT_ID",
881
0
                    OFTInteger, 6, 0, NULL);
882
0
            }
883
0
            else if (iType == NRT_LINEREC)
884
0
            {
885
0
                poPReader->EstablishLayer(
886
0
                    "GENERIC_LINE",
887
0
                    OGR_GT_SetModifier(wkbLineString, bHasZ, FALSE),
888
0
                    TranslateGenericLine, NRT_LINEREC, poClass, "LINE_ID",
889
0
                    OFTInteger, 6, 0, NULL);
890
0
            }
891
0
            else if (iType == NRT_TEXTREC)
892
0
            {
893
0
                poPReader->EstablishLayer(
894
0
                    "GENERIC_TEXT", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
895
0
                    TranslateGenericText, NRT_TEXTREC, poClass, "TEXT_ID",
896
0
                    OFTInteger, 6, 0, NULL);
897
0
            }
898
0
            else if (iType == NRT_NAMEREC)
899
0
            {
900
0
                poPReader->EstablishLayer(
901
0
                    "GENERIC_NAME", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
902
0
                    TranslateGenericName, NRT_NAMEREC, poClass, "NAME_ID",
903
0
                    OFTInteger, 6, 0, NULL);
904
0
            }
905
0
            else if (iType == NRT_NODEREC)
906
0
            {
907
0
                poPReader->EstablishLayer(
908
0
                    "GENERIC_NODE", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
909
0
                    TranslateGenericNode, NRT_NODEREC, poClass, "NODE_ID",
910
0
                    OFTInteger, 6, 0, "NUM_LINKS", OFTInteger, 4, 0,
911
0
                    "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, "DIR",
912
0
                    OFTIntegerList, 1, 0, NULL);
913
0
            }
914
0
            else if (iType == NRT_COLLECT)
915
0
            {
916
0
                poPReader->EstablishLayer(
917
0
                    "GENERIC_COLLECTION", wkbNone, TranslateGenericCollection,
918
0
                    NRT_COLLECT, poClass, "COLL_ID", OFTInteger, 6, 0,
919
0
                    "NUM_PARTS", OFTInteger, 4, 0, "TYPE", OFTIntegerList, 2, 0,
920
0
                    "ID", OFTIntegerList, 6, 0, NULL);
921
0
            }
922
0
            else if (iType == NRT_POLYGON)
923
0
            {
924
0
                poPReader->EstablishLayer(
925
0
                    "GENERIC_POLY", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
926
0
                    TranslateGenericPoly, NRT_POLYGON, poClass, "POLY_ID",
927
0
                    OFTInteger, 6, 0, "NUM_PARTS", OFTInteger, 4, 0, "DIR",
928
0
                    OFTIntegerList, 1, 0, "GEOM_ID_OF_LINK", OFTIntegerList, 6,
929
0
                    0, "RingStart", OFTIntegerList, 6, 0, NULL);
930
0
            }
931
0
            else if (iType == NRT_CPOLY)
932
0
            {
933
0
                poPReader->EstablishLayer(
934
0
                    "GENERIC_CPOLY", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
935
0
                    TranslateGenericCPoly, NRT_CPOLY, poClass, "CPOLY_ID",
936
0
                    OFTInteger, 6, 0, "NUM_PARTS", OFTInteger, 4, 0, "POLY_ID",
937
                    OFTIntegerList, 1, 0, NULL);
938
0
            }
939
0
        }
940
0
    }
941
0
}