Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/s57/s57reader.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  S-57 Translator
4
 * Purpose:  Implements S57Reader class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, 2001, Frank Warmerdam
9
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_conv.h"
15
#include "cpl_string.h"
16
#include "ogr_api.h"
17
#include "s57.h"
18
19
#include <cmath>
20
21
#include <algorithm>
22
#include <string>
23
24
/**
25
 * Recode the given string from a source encoding to UTF-8 encoding.  The source
26
 * encoding is established by inspecting the AALL and NALL fields of the S57
27
 * DSSI record. If first time, the DSSI is read to setup appropriate
28
 * variables. Main scope of this function is to have the strings of all
29
 * attributes encoded/recoded to the same codepage in the final Shapefiles .DBF.
30
 *
31
 * @param[in] SourceString source string to be recoded to UTF-8.
32
 *     LookAtAALL-NALL: flag indicating if the string becomes from an
33
 *     international attribute (e.g.  INFORM, OBJNAM) or national attribute (e.g
34
 *     NINFOM, NOBJNM). The type of encoding is contained in two different
35
 *     fields of the S57 DSSI record: AALL for the international attributes,
36
 *     NAAL for the national ones, so depending on the type of encoding,
37
 *     different fields must be checked to fetch in which way the source string
38
 *     is encoded.
39
 *
40
 *     0: the type of endoding is for international attributes
41
 *     1: the type of endoding is for national attributes
42
 *
43
 * @param[in] LookAtAALL_NALL to be documented
44
 *
45
 * @return the output string recoded to UTF-8 or left unchanged if no valid
46
 *     recoding applicable. The recodinf relies on GDAL functions appropriately
47
 *     called, which allocate themselves the necessary memory to hold the
48
 *     recoded string.
49
 * NOTE: Aall variable is currently not used.
50
 *******************************************************************************/
51
char *S57Reader::RecodeByDSSI(const char *SourceString, bool LookAtAALL_NALL)
52
0
{
53
0
    if (needAallNallSetup == true)
54
0
    {
55
0
        OGRFeature *dsidFeature = ReadDSID();
56
0
        if (dsidFeature == nullptr)
57
0
            return CPLStrdup(SourceString);
58
0
        Aall = dsidFeature->GetFieldAsInteger("DSSI_AALL");
59
0
        Nall = dsidFeature->GetFieldAsInteger("DSSI_NALL");
60
0
        CPLDebug("S57", "DSSI_AALL = %d, DSSI_NALL = %d", Aall, Nall);
61
0
        needAallNallSetup = false;
62
0
        delete dsidFeature;
63
0
    }
64
65
0
    char *RecodedString = nullptr;
66
0
    if (!LookAtAALL_NALL)
67
0
    {
68
        // In case of international attributes, only ISO8859-1 code page is
69
        // used (standard ascii). The result is identical to the source string
70
        // if it contains 0..127 ascii code (LL0), can slightly differ if it
71
        // contains diacritics 0..255 ascii codes (LL1).
72
0
        RecodedString =
73
0
            CPLRecode(SourceString, CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
74
0
    }
75
0
    else
76
0
    {
77
0
        if (Nall == 2)  // national string encoded in UCS-2
78
0
        {
79
0
            GByte *pabyStr =
80
0
                reinterpret_cast<GByte *>(const_cast<char *>(SourceString));
81
82
            /* Count the number of characters */
83
0
            int i = 0;
84
0
            while (!((pabyStr[2 * i] == DDF_UNIT_TERMINATOR &&
85
0
                      pabyStr[2 * i + 1] == 0) ||
86
0
                     (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0)))
87
0
                i++;
88
89
0
            wchar_t *wideString =
90
0
                static_cast<wchar_t *>(CPLMalloc((i + 1) * sizeof(wchar_t)));
91
0
            i = 0;
92
0
            bool bLittleEndian = true;
93
94
            /* Skip BOM */
95
0
            if (pabyStr[0] == 0xFF && pabyStr[1] == 0xFE)
96
0
                i++;
97
0
            else if (pabyStr[0] == 0xFE && pabyStr[1] == 0xFF)
98
0
            {
99
0
                bLittleEndian = false;
100
0
                i++;
101
0
            }
102
103
0
            int j = 0;
104
0
            while (!((pabyStr[2 * i] == DDF_UNIT_TERMINATOR &&
105
0
                      pabyStr[2 * i + 1] == 0) ||
106
0
                     (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0)))
107
0
            {
108
0
                if (bLittleEndian)
109
0
                    wideString[j++] =
110
0
                        pabyStr[i * 2] | (pabyStr[i * 2 + 1] << 8);
111
0
                else
112
0
                    wideString[j++] =
113
0
                        pabyStr[i * 2 + 1] | (pabyStr[i * 2] << 8);
114
0
                i++;
115
0
            }
116
0
            wideString[j] = 0;
117
0
            RecodedString =
118
0
                CPLRecodeFromWChar(wideString, CPL_ENC_UCS2, CPL_ENC_UTF8);
119
0
            CPLFree(wideString);
120
0
        }
121
0
        else
122
0
        {
123
            // National string encoded as ISO8859-1.
124
            // See comment for above on LL0/LL1).
125
0
            RecodedString =
126
0
                CPLRecode(SourceString, CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
127
0
        }
128
0
    }
129
130
0
    if (RecodedString == nullptr)
131
0
        RecodedString = CPLStrdup(SourceString);
132
133
0
    return RecodedString;
134
0
}
135
136
/************************************************************************/
137
/*                             S57Reader()                              */
138
/************************************************************************/
139
140
S57Reader::S57Reader(const char *pszFilename)
141
506
    : poRegistrar(nullptr), poClassContentExplorer(nullptr), nFDefnCount(0),
142
506
      papoFDefnList(nullptr), pszModuleName(CPLStrdup(pszFilename)),
143
506
      pszDSNM(nullptr), poModule(nullptr), nCOMF(1000000), nSOMF(10),
144
506
      bFileIngested(false), nNextVIIndex(0), nNextVCIndex(0), nNextVEIndex(0),
145
506
      nNextVFIndex(0), nNextFEIndex(0), nNextDSIDIndex(0),
146
506
      poDSIDRecord(nullptr), poDSPMRecord(nullptr), papszOptions(nullptr),
147
506
      nOptionFlags(S57M_UPDATES), iPointOffset(0), poMultiPoint(nullptr),
148
506
      Aall(0),                  // See RecodeByDSSI() function.
149
506
      Nall(0),                  // See RecodeByDSSI() function.
150
506
      needAallNallSetup(true),  // See RecodeByDSSI() function.
151
506
      bMissingWarningIssued(false), bAttrWarningIssued(false)
152
506
{
153
506
}
154
155
/************************************************************************/
156
/*                             ~S57Reader()                             */
157
/************************************************************************/
158
159
S57Reader::~S57Reader()
160
161
506
{
162
506
    Close();
163
164
506
    CPLFree(pszModuleName);
165
506
    CSLDestroy(papszOptions);
166
167
506
    CPLFree(papoFDefnList);
168
506
}
169
170
/************************************************************************/
171
/*                                Open()                                */
172
/************************************************************************/
173
174
int S57Reader::Open(int bTestOpen)
175
176
506
{
177
506
    if (poModule != nullptr)
178
0
    {
179
0
        Rewind();
180
0
        return TRUE;
181
0
    }
182
183
506
    poModule = new DDFModule();
184
506
    if (!poModule->Open(pszModuleName))
185
49
    {
186
        // notdef: test bTestOpen.
187
49
        delete poModule;
188
49
        poModule = nullptr;
189
49
        return FALSE;
190
49
    }
191
192
    // note that the following won't work for catalogs.
193
457
    if (poModule->FindFieldDefn("DSID") == nullptr)
194
29
    {
195
29
        if (!bTestOpen)
196
0
        {
197
0
            CPLError(CE_Failure, CPLE_AppDefined,
198
0
                     "%s is an ISO8211 file, but not an S-57 data file.\n",
199
0
                     pszModuleName);
200
0
        }
201
29
        delete poModule;
202
29
        poModule = nullptr;
203
29
        return FALSE;
204
29
    }
205
206
    // Make sure the FSPT field is marked as repeating.
207
428
    DDFFieldDefn *poFSPT = poModule->FindFieldDefn("FSPT");
208
428
    if (poFSPT != nullptr && !poFSPT->IsRepeating())
209
205
    {
210
205
        CPLDebug("S57", "Forcing FSPT field to be repeating.");
211
205
        poFSPT->SetRepeatingFlag(TRUE);
212
205
    }
213
214
428
    nNextFEIndex = 0;
215
428
    nNextVIIndex = 0;
216
428
    nNextVCIndex = 0;
217
428
    nNextVEIndex = 0;
218
428
    nNextVFIndex = 0;
219
428
    nNextDSIDIndex = 0;
220
221
428
    return TRUE;
222
457
}
223
224
/************************************************************************/
225
/*                               Close()                                */
226
/************************************************************************/
227
228
void S57Reader::Close()
229
230
506
{
231
506
    if (poModule != nullptr)
232
428
    {
233
428
        oVI_Index.Clear();
234
428
        oVC_Index.Clear();
235
428
        oVE_Index.Clear();
236
428
        oVF_Index.Clear();
237
428
        oFE_Index.Clear();
238
239
428
        if (poDSIDRecord != nullptr)
240
251
        {
241
251
            delete poDSIDRecord;
242
251
            poDSIDRecord = nullptr;
243
251
        }
244
428
        if (poDSPMRecord != nullptr)
245
19
        {
246
19
            delete poDSPMRecord;
247
19
            poDSPMRecord = nullptr;
248
19
        }
249
250
428
        ClearPendingMultiPoint();
251
252
428
        delete poModule;
253
428
        poModule = nullptr;
254
255
428
        bFileIngested = false;
256
257
428
        CPLFree(pszDSNM);
258
428
        pszDSNM = nullptr;
259
428
    }
260
506
}
261
262
/************************************************************************/
263
/*                       ClearPendingMultiPoint()                       */
264
/************************************************************************/
265
266
void S57Reader::ClearPendingMultiPoint()
267
268
704
{
269
704
    if (poMultiPoint != nullptr)
270
0
    {
271
0
        delete poMultiPoint;
272
0
        poMultiPoint = nullptr;
273
0
    }
274
704
}
275
276
/************************************************************************/
277
/*                       NextPendingMultiPoint()                        */
278
/************************************************************************/
279
280
OGRFeature *S57Reader::NextPendingMultiPoint()
281
282
0
{
283
0
    CPLAssert(poMultiPoint != nullptr);
284
0
    CPLAssert(wkbFlatten(poMultiPoint->GetGeometryRef()->getGeometryType()) ==
285
0
              wkbMultiPoint);
286
287
0
    OGRFeatureDefn *poDefn = poMultiPoint->GetDefnRef();
288
0
    OGRFeature *poPoint = new OGRFeature(poDefn);
289
0
    OGRMultiPoint *poMPGeom = poMultiPoint->GetGeometryRef()->toMultiPoint();
290
291
0
    poPoint->SetFID(poMultiPoint->GetFID());
292
293
0
    for (int i = 0; i < poDefn->GetFieldCount(); i++)
294
0
    {
295
0
        poPoint->SetField(i, poMultiPoint->GetRawFieldRef(i));
296
0
    }
297
298
0
    OGRPoint *poSrcPoint = poMPGeom->getGeometryRef(iPointOffset);
299
0
    iPointOffset++;
300
0
    poPoint->SetGeometry(poSrcPoint);
301
302
0
    if ((nOptionFlags & S57M_ADD_SOUNDG_DEPTH))
303
0
        poPoint->SetField("DEPTH", poSrcPoint->getZ());
304
305
0
    if (iPointOffset >= poMPGeom->getNumGeometries())
306
0
        ClearPendingMultiPoint();
307
308
0
    return poPoint;
309
0
}
310
311
/************************************************************************/
312
/*                             SetOptions()                             */
313
/************************************************************************/
314
315
bool S57Reader::SetOptions(char **papszOptionsIn)
316
317
506
{
318
506
    CSLDestroy(papszOptions);
319
506
    papszOptions = CSLDuplicate(papszOptionsIn);
320
321
506
    const char *pszOptionValue =
322
506
        CSLFetchNameValue(papszOptions, S57O_SPLIT_MULTIPOINT);
323
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
324
0
        nOptionFlags |= S57M_SPLIT_MULTIPOINT;
325
506
    else
326
506
        nOptionFlags &= ~S57M_SPLIT_MULTIPOINT;
327
328
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_ADD_SOUNDG_DEPTH);
329
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
330
0
        nOptionFlags |= S57M_ADD_SOUNDG_DEPTH;
331
506
    else
332
506
        nOptionFlags &= ~S57M_ADD_SOUNDG_DEPTH;
333
334
506
    if ((nOptionFlags & S57M_ADD_SOUNDG_DEPTH) &&
335
506
        !(nOptionFlags & S57M_SPLIT_MULTIPOINT))
336
0
    {
337
0
        CPLError(CE_Failure, CPLE_AppDefined,
338
0
                 "Inconsistent options : ADD_SOUNDG_DEPTH should only be "
339
0
                 "enabled if SPLIT_MULTIPOINT is also enabled");
340
0
        return false;
341
0
    }
342
343
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_LNAM_REFS);
344
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
345
506
        nOptionFlags |= S57M_LNAM_REFS;
346
0
    else
347
0
        nOptionFlags &= ~S57M_LNAM_REFS;
348
349
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_UPDATES);
350
506
    if (pszOptionValue == nullptr)
351
506
        /* no change */;
352
0
    else if (!EQUAL(pszOptionValue, "APPLY"))
353
0
        nOptionFlags &= ~S57M_UPDATES;
354
0
    else
355
0
        nOptionFlags |= S57M_UPDATES;
356
357
506
    pszOptionValue =
358
506
        CSLFetchNameValue(papszOptions, S57O_PRESERVE_EMPTY_NUMBERS);
359
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
360
0
        nOptionFlags |= S57M_PRESERVE_EMPTY_NUMBERS;
361
506
    else
362
506
        nOptionFlags &= ~S57M_PRESERVE_EMPTY_NUMBERS;
363
364
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RETURN_PRIMITIVES);
365
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
366
0
        nOptionFlags |= S57M_RETURN_PRIMITIVES;
367
506
    else
368
506
        nOptionFlags &= ~S57M_RETURN_PRIMITIVES;
369
370
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RETURN_LINKAGES);
371
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
372
0
        nOptionFlags |= S57M_RETURN_LINKAGES;
373
506
    else
374
506
        nOptionFlags &= ~S57M_RETURN_LINKAGES;
375
376
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RETURN_DSID);
377
506
    if (pszOptionValue == nullptr || CPLTestBool(pszOptionValue))
378
506
        nOptionFlags |= S57M_RETURN_DSID;
379
0
    else
380
0
        nOptionFlags &= ~S57M_RETURN_DSID;
381
382
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RECODE_BY_DSSI);
383
506
    if (pszOptionValue == nullptr || CPLTestBool(pszOptionValue))
384
506
        nOptionFlags |= S57M_RECODE_BY_DSSI;
385
0
    else
386
0
        nOptionFlags &= ~S57M_RECODE_BY_DSSI;
387
388
506
    pszOptionValue = CSLFetchNameValue(papszOptions, S57O_LIST_AS_STRING);
389
506
    if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue))
390
0
        nOptionFlags |= S57M_LIST_AS_STRING;
391
506
    else
392
506
        nOptionFlags &= ~S57M_LIST_AS_STRING;
393
394
506
    return true;
395
506
}
396
397
/************************************************************************/
398
/*                           SetClassBased()                            */
399
/************************************************************************/
400
401
void S57Reader::SetClassBased(S57ClassRegistrar *poReg,
402
                              S57ClassContentExplorer *poClassContentExplorerIn)
403
404
0
{
405
0
    poRegistrar = poReg;
406
0
    poClassContentExplorer = poClassContentExplorerIn;
407
0
}
408
409
/************************************************************************/
410
/*                               Rewind()                               */
411
/************************************************************************/
412
413
void S57Reader::Rewind()
414
415
0
{
416
0
    ClearPendingMultiPoint();
417
0
    nNextFEIndex = 0;
418
0
    nNextVIIndex = 0;
419
0
    nNextVCIndex = 0;
420
0
    nNextVEIndex = 0;
421
0
    nNextVFIndex = 0;
422
0
    nNextDSIDIndex = 0;
423
0
}
424
425
/************************************************************************/
426
/*                               Ingest()                               */
427
/*                                                                      */
428
/*      Read all the records into memory, adding to the appropriate     */
429
/*      indexes.                                                        */
430
/************************************************************************/
431
432
bool S57Reader::Ingest()
433
434
1.37k
{
435
1.37k
    if (poModule == nullptr || bFileIngested)
436
0
        return true;
437
438
    /* -------------------------------------------------------------------- */
439
    /*      Read all the records in the module, and place them in           */
440
    /*      appropriate indexes.                                            */
441
    /* -------------------------------------------------------------------- */
442
1.37k
    CPLErrorReset();
443
1.37k
    DDFRecord *poRecord = nullptr;
444
765k
    while ((poRecord = poModule->ReadRecord()) != nullptr)
445
764k
    {
446
764k
        DDFField *poKeyField = poRecord->GetField(1);
447
764k
        if (poKeyField == nullptr)
448
4
            return false;
449
764k
        DDFFieldDefn *poKeyFieldDefn = poKeyField->GetFieldDefn();
450
764k
        if (poKeyFieldDefn == nullptr)
451
87
            continue;
452
763k
        const char *pszName = poKeyFieldDefn->GetName();
453
763k
        if (pszName == nullptr)
454
0
            continue;
455
456
763k
        if (EQUAL(pszName, "VRID"))
457
1.45k
        {
458
1.45k
            int bSuccess = FALSE;
459
1.45k
            const int nRCNM =
460
1.45k
                poRecord->GetIntSubfield("VRID", 0, "RCNM", 0, &bSuccess);
461
1.45k
            if (!bSuccess && CPLGetLastErrorType() == CE_Failure)
462
24
                break;
463
1.43k
            const int nRCID =
464
1.43k
                poRecord->GetIntSubfield("VRID", 0, "RCID", 0, &bSuccess);
465
1.43k
            if (!bSuccess && CPLGetLastErrorType() == CE_Failure)
466
4
                break;
467
468
1.42k
            switch (nRCNM)
469
1.42k
            {
470
1.05k
                case RCNM_VI:
471
1.05k
                    oVI_Index.AddRecord(nRCID, poRecord->Clone());
472
1.05k
                    break;
473
474
183
                case RCNM_VC:
475
183
                    oVC_Index.AddRecord(nRCID, poRecord->Clone());
476
183
                    break;
477
478
110
                case RCNM_VE:
479
110
                    oVE_Index.AddRecord(nRCID, poRecord->Clone());
480
110
                    break;
481
482
1
                case RCNM_VF:
483
1
                    oVF_Index.AddRecord(nRCID, poRecord->Clone());
484
1
                    break;
485
486
85
                default:
487
85
                    CPLError(CE_Failure, CPLE_AppDefined,
488
85
                             "Unhandled value for RCNM ; %d", nRCNM);
489
85
                    break;
490
1.42k
            }
491
1.42k
        }
492
493
762k
        else if (EQUAL(pszName, "FRID"))
494
695k
        {
495
695k
            int bSuccess = FALSE;
496
695k
            int nRCID =
497
695k
                poRecord->GetIntSubfield("FRID", 0, "RCID", 0, &bSuccess);
498
695k
            if (!bSuccess && CPLGetLastErrorType() == CE_Failure)
499
12
                break;
500
501
695k
            oFE_Index.AddRecord(nRCID, poRecord->Clone());
502
695k
        }
503
504
67.3k
        else if (EQUAL(pszName, "DSID"))
505
67.3k
        {
506
67.3k
            int bSuccess = FALSE;
507
67.3k
            CPLFree(pszDSNM);
508
67.3k
            pszDSNM = CPLStrdup(
509
67.3k
                poRecord->GetStringSubfield("DSID", 0, "DSNM", 0, &bSuccess));
510
67.3k
            if (!bSuccess && CPLGetLastErrorType() == CE_Failure)
511
7
                break;
512
513
67.3k
            const char *pszEDTN =
514
67.3k
                poRecord->GetStringSubfield("DSID", 0, "EDTN", 0);
515
67.3k
            if (pszEDTN)
516
27.1k
                m_osEDTNUpdate = pszEDTN;
517
518
67.3k
            const char *pszUPDN =
519
67.3k
                poRecord->GetStringSubfield("DSID", 0, "UPDN", 0);
520
67.3k
            if (pszUPDN)
521
12.4k
                m_osUPDNUpdate = pszUPDN;
522
523
67.3k
            const char *pszISDT =
524
67.3k
                poRecord->GetStringSubfield("DSID", 0, "ISDT", 0);
525
67.3k
            if (pszISDT)
526
4.89k
                m_osISDTUpdate = pszISDT;
527
528
67.3k
            if (nOptionFlags & S57M_RETURN_DSID)
529
67.3k
            {
530
67.3k
                if (poDSIDRecord != nullptr)
531
67.0k
                    delete poDSIDRecord;
532
533
67.3k
                poDSIDRecord = poRecord->Clone();
534
67.3k
            }
535
67.3k
        }
536
537
25
        else if (EQUAL(pszName, "DSPM"))
538
25
        {
539
25
            int bSuccess = FALSE;
540
25
            nCOMF = std::max(
541
25
                1, poRecord->GetIntSubfield("DSPM", 0, "COMF", 0, &bSuccess));
542
25
            if (!bSuccess && CPLGetLastErrorType() == CE_Failure)
543
0
                break;
544
25
            nSOMF = std::max(
545
25
                1, poRecord->GetIntSubfield("DSPM", 0, "SOMF", 0, &bSuccess));
546
25
            if (!bSuccess && CPLGetLastErrorType() == CE_Failure)
547
0
                break;
548
549
25
            if (nOptionFlags & S57M_RETURN_DSID)
550
25
            {
551
25
                if (poDSPMRecord != nullptr)
552
6
                    delete poDSPMRecord;
553
554
25
                poDSPMRecord = poRecord->Clone();
555
25
            }
556
25
        }
557
558
0
        else
559
0
        {
560
0
            CPLDebug("S57", "Skipping %s record in S57Reader::Ingest().",
561
0
                     pszName);
562
0
        }
563
763k
    }
564
565
1.36k
    if (CPLGetLastErrorType() == CE_Failure)
566
1.05k
        return false;
567
568
316
    bFileIngested = true;
569
570
    /* -------------------------------------------------------------------- */
571
    /*      If update support is enabled, read and apply them.              */
572
    /* -------------------------------------------------------------------- */
573
316
    if (nOptionFlags & S57M_UPDATES)
574
316
        return FindAndApplyUpdates();
575
576
0
    return true;
577
316
}
578
579
/************************************************************************/
580
/*                           SetNextFEIndex()                           */
581
/************************************************************************/
582
583
void S57Reader::SetNextFEIndex(int nNewIndex, int nRCNM)
584
585
15.2k
{
586
15.2k
    if (nRCNM == RCNM_VI)
587
0
        nNextVIIndex = nNewIndex;
588
15.2k
    else if (nRCNM == RCNM_VC)
589
0
        nNextVCIndex = nNewIndex;
590
15.2k
    else if (nRCNM == RCNM_VE)
591
0
        nNextVEIndex = nNewIndex;
592
15.2k
    else if (nRCNM == RCNM_VF)
593
0
        nNextVFIndex = nNewIndex;
594
15.2k
    else if (nRCNM == RCNM_DSID)
595
428
        nNextDSIDIndex = nNewIndex;
596
14.8k
    else
597
14.8k
    {
598
14.8k
        if (nNextFEIndex != nNewIndex)
599
276
            ClearPendingMultiPoint();
600
601
14.8k
        nNextFEIndex = nNewIndex;
602
14.8k
    }
603
15.2k
}
604
605
/************************************************************************/
606
/*                           GetNextFEIndex()                           */
607
/************************************************************************/
608
609
int S57Reader::GetNextFEIndex(int nRCNM)
610
611
15.2k
{
612
15.2k
    if (nRCNM == RCNM_VI)
613
0
        return nNextVIIndex;
614
15.2k
    if (nRCNM == RCNM_VC)
615
0
        return nNextVCIndex;
616
15.2k
    if (nRCNM == RCNM_VE)
617
0
        return nNextVEIndex;
618
15.2k
    if (nRCNM == RCNM_VF)
619
0
        return nNextVFIndex;
620
15.2k
    if (nRCNM == RCNM_DSID)
621
428
        return nNextDSIDIndex;
622
623
14.8k
    return nNextFEIndex;
624
15.2k
}
625
626
/************************************************************************/
627
/*                          ReadNextFeature()                           */
628
/************************************************************************/
629
630
OGRFeature *S57Reader::ReadNextFeature(OGRFeatureDefn *poTarget)
631
632
15.2k
{
633
15.2k
    if (!bFileIngested && !Ingest())
634
1.37k
        return nullptr;
635
636
    /* -------------------------------------------------------------------- */
637
    /*      Special case for "in progress" multipoints being split up.      */
638
    /* -------------------------------------------------------------------- */
639
13.9k
    if (poMultiPoint != nullptr)
640
0
    {
641
0
        if (poTarget == nullptr || poTarget == poMultiPoint->GetDefnRef())
642
0
        {
643
0
            return NextPendingMultiPoint();
644
0
        }
645
0
        else
646
0
        {
647
0
            ClearPendingMultiPoint();
648
0
        }
649
0
    }
650
651
    /* -------------------------------------------------------------------- */
652
    /*      Next vector feature?                                            */
653
    /* -------------------------------------------------------------------- */
654
13.9k
    if ((nOptionFlags & S57M_RETURN_DSID) && nNextDSIDIndex == 0 &&
655
13.9k
        (poTarget == nullptr || EQUAL(poTarget->GetName(), "DSID")))
656
0
    {
657
0
        return ReadDSID();
658
0
    }
659
660
    /* -------------------------------------------------------------------- */
661
    /*      Next vector feature?                                            */
662
    /* -------------------------------------------------------------------- */
663
13.9k
    if (nOptionFlags & S57M_RETURN_PRIMITIVES)
664
0
    {
665
0
        int nRCNM = 0;
666
0
        int *pnCounter = nullptr;
667
668
0
        if (poTarget == nullptr)
669
0
        {
670
0
            if (nNextVIIndex < oVI_Index.GetCount())
671
0
            {
672
0
                nRCNM = RCNM_VI;
673
0
                pnCounter = &nNextVIIndex;
674
0
            }
675
0
            else if (nNextVCIndex < oVC_Index.GetCount())
676
0
            {
677
0
                nRCNM = RCNM_VC;
678
0
                pnCounter = &nNextVCIndex;
679
0
            }
680
0
            else if (nNextVEIndex < oVE_Index.GetCount())
681
0
            {
682
0
                nRCNM = RCNM_VE;
683
0
                pnCounter = &nNextVEIndex;
684
0
            }
685
0
            else if (nNextVFIndex < oVF_Index.GetCount())
686
0
            {
687
0
                nRCNM = RCNM_VF;
688
0
                pnCounter = &nNextVFIndex;
689
0
            }
690
0
        }
691
0
        else
692
0
        {
693
0
            if (EQUAL(poTarget->GetName(), OGRN_VI))
694
0
            {
695
0
                nRCNM = RCNM_VI;
696
0
                pnCounter = &nNextVIIndex;
697
0
            }
698
0
            else if (EQUAL(poTarget->GetName(), OGRN_VC))
699
0
            {
700
0
                nRCNM = RCNM_VC;
701
0
                pnCounter = &nNextVCIndex;
702
0
            }
703
0
            else if (EQUAL(poTarget->GetName(), OGRN_VE))
704
0
            {
705
0
                nRCNM = RCNM_VE;
706
0
                pnCounter = &nNextVEIndex;
707
0
            }
708
0
            else if (EQUAL(poTarget->GetName(), OGRN_VF))
709
0
            {
710
0
                nRCNM = RCNM_VF;
711
0
                pnCounter = &nNextVFIndex;
712
0
            }
713
0
        }
714
715
0
        if (nRCNM != 0)
716
0
        {
717
0
            OGRFeature *poFeature = ReadVector(*pnCounter, nRCNM);
718
0
            if (poFeature != nullptr)
719
0
            {
720
0
                *pnCounter += 1;
721
0
                return poFeature;
722
0
            }
723
0
        }
724
0
    }
725
726
    /* -------------------------------------------------------------------- */
727
    /*      Next feature.                                                   */
728
    /* -------------------------------------------------------------------- */
729
2.31M
    while (nNextFEIndex < oFE_Index.GetCount())
730
2.30M
    {
731
2.30M
        OGRFeatureDefn *poFeatureDefn = static_cast<OGRFeatureDefn *>(
732
2.30M
            oFE_Index.GetClientInfoByIndex(nNextFEIndex));
733
734
2.30M
        if (poFeatureDefn == nullptr)
735
694k
        {
736
694k
            poFeatureDefn = FindFDefn(oFE_Index.GetByIndex(nNextFEIndex));
737
694k
            oFE_Index.SetClientInfoByIndex(nNextFEIndex, poFeatureDefn);
738
694k
        }
739
740
2.30M
        if (poFeatureDefn != poTarget && poTarget != nullptr)
741
2.29M
        {
742
2.29M
            nNextFEIndex++;
743
2.29M
            continue;
744
2.29M
        }
745
746
13.1k
        OGRFeature *poFeature = ReadFeature(nNextFEIndex++, poTarget);
747
13.1k
        if (poFeature != nullptr)
748
13.1k
        {
749
13.1k
            if ((nOptionFlags & S57M_SPLIT_MULTIPOINT) &&
750
13.1k
                poFeature->GetGeometryRef() != nullptr &&
751
13.1k
                wkbFlatten(poFeature->GetGeometryRef()->getGeometryType()) ==
752
0
                    wkbMultiPoint)
753
0
            {
754
0
                poMultiPoint = poFeature;
755
0
                iPointOffset = 0;
756
0
                return NextPendingMultiPoint();
757
0
            }
758
759
13.1k
            return poFeature;
760
13.1k
        }
761
13.1k
    }
762
763
769
    return nullptr;
764
13.9k
}
765
766
/************************************************************************/
767
/*                            ReadFeature()                             */
768
/*                                                                      */
769
/*      Read the features who's id is provided.                         */
770
/************************************************************************/
771
772
OGRFeature *S57Reader::ReadFeature(int nFeatureId, OGRFeatureDefn *poTarget)
773
774
13.1k
{
775
13.1k
    if (nFeatureId < 0 || nFeatureId >= oFE_Index.GetCount())
776
0
        return nullptr;
777
778
13.1k
    OGRFeature *poFeature = nullptr;
779
780
13.1k
    if ((nOptionFlags & S57M_RETURN_DSID) && nFeatureId == 0 &&
781
13.1k
        (poTarget == nullptr || EQUAL(poTarget->GetName(), "DSID")))
782
0
    {
783
0
        poFeature = ReadDSID();
784
0
    }
785
13.1k
    else
786
13.1k
    {
787
13.1k
        poFeature = AssembleFeature(oFE_Index.GetByIndex(nFeatureId), poTarget);
788
13.1k
    }
789
13.1k
    if (poFeature != nullptr)
790
13.1k
        poFeature->SetFID(nFeatureId);
791
792
13.1k
    return poFeature;
793
13.1k
}
794
795
/************************************************************************/
796
/*                          AssembleFeature()                           */
797
/*                                                                      */
798
/*      Assemble an OGR feature based on a feature record.              */
799
/************************************************************************/
800
801
OGRFeature *S57Reader::AssembleFeature(DDFRecord *poRecord,
802
                                       OGRFeatureDefn *poTarget)
803
804
13.1k
{
805
    /* -------------------------------------------------------------------- */
806
    /*      Find the feature definition to use.  Currently this is based    */
807
    /*      on the primitive, but eventually this should be based on the    */
808
    /*      object class (FRID.OBJL) in some cases, and the primitive in    */
809
    /*      others.                                                         */
810
    /* -------------------------------------------------------------------- */
811
13.1k
    OGRFeatureDefn *poFDefn = FindFDefn(poRecord);
812
13.1k
    if (poFDefn == nullptr)
813
0
        return nullptr;
814
815
    /* -------------------------------------------------------------------- */
816
    /*      Does this match our target feature definition?  If not skip     */
817
    /*      this feature.                                                   */
818
    /* -------------------------------------------------------------------- */
819
13.1k
    if (poTarget != nullptr && poFDefn != poTarget)
820
0
        return nullptr;
821
822
    /* -------------------------------------------------------------------- */
823
    /*      Create the new feature object.                                  */
824
    /* -------------------------------------------------------------------- */
825
13.1k
    OGRFeature *poFeature = new OGRFeature(poFDefn);
826
827
    /* -------------------------------------------------------------------- */
828
    /*      Assign a few standard feature attributes.                        */
829
    /* -------------------------------------------------------------------- */
830
13.1k
    int nOBJL = poRecord->GetIntSubfield("FRID", 0, "OBJL", 0);
831
13.1k
    poFeature->SetField("OBJL", nOBJL);
832
833
13.1k
    poFeature->SetField("RCID", poRecord->GetIntSubfield("FRID", 0, "RCID", 0));
834
13.1k
    poFeature->SetField("PRIM", poRecord->GetIntSubfield("FRID", 0, "PRIM", 0));
835
13.1k
    poFeature->SetField("GRUP", poRecord->GetIntSubfield("FRID", 0, "GRUP", 0));
836
13.1k
    poFeature->SetField("RVER", poRecord->GetIntSubfield("FRID", 0, "RVER", 0));
837
13.1k
    poFeature->SetField("AGEN", poRecord->GetIntSubfield("FOID", 0, "AGEN", 0));
838
13.1k
    poFeature->SetField("FIDN", poRecord->GetIntSubfield("FOID", 0, "FIDN", 0));
839
13.1k
    poFeature->SetField("FIDS", poRecord->GetIntSubfield("FOID", 0, "FIDS", 0));
840
841
    /* -------------------------------------------------------------------- */
842
    /*      Generate long name, if requested.                               */
843
    /* -------------------------------------------------------------------- */
844
13.1k
    if (nOptionFlags & S57M_LNAM_REFS)
845
13.1k
    {
846
13.1k
        GenerateLNAMAndRefs(poRecord, poFeature);
847
13.1k
    }
848
849
    /* -------------------------------------------------------------------- */
850
    /*      Generate primitive references if requested.                     */
851
    /* -------------------------------------------------------------------- */
852
13.1k
    if (nOptionFlags & S57M_RETURN_LINKAGES)
853
0
        GenerateFSPTAttributes(poRecord, poFeature);
854
855
    /* -------------------------------------------------------------------- */
856
    /*      Apply object class specific attributes, if supported.           */
857
    /* -------------------------------------------------------------------- */
858
13.1k
    if (poRegistrar != nullptr)
859
0
        ApplyObjectClassAttributes(poRecord, poFeature);
860
861
    /* -------------------------------------------------------------------- */
862
    /*      Find and assign spatial component.                              */
863
    /* -------------------------------------------------------------------- */
864
13.1k
    const int nPRIM = poRecord->GetIntSubfield("FRID", 0, "PRIM", 0);
865
866
13.1k
    if (nPRIM == PRIM_P)
867
3.19k
    {
868
3.19k
        if (nOBJL == 129) /* SOUNDG */
869
56
            AssembleSoundingGeometry(poRecord, poFeature);
870
3.13k
        else
871
3.13k
            AssemblePointGeometry(poRecord, poFeature);
872
3.19k
    }
873
9.93k
    else if (nPRIM == PRIM_L)
874
3.88k
    {
875
3.88k
        AssembleLineGeometry(poRecord, poFeature);
876
3.88k
    }
877
6.05k
    else if (nPRIM == PRIM_A)
878
6.05k
    {
879
6.05k
        AssembleAreaGeometry(poRecord, poFeature);
880
6.05k
    }
881
882
13.1k
    return poFeature;
883
13.1k
}
884
885
/************************************************************************/
886
/*                     ApplyObjectClassAttributes()                     */
887
/************************************************************************/
888
889
void S57Reader::ApplyObjectClassAttributes(DDFRecord *poRecord,
890
                                           OGRFeature *poFeature)
891
892
0
{
893
    /* -------------------------------------------------------------------- */
894
    /*      ATTF Attributes                                                 */
895
    /* -------------------------------------------------------------------- */
896
0
    DDFField *poATTF = poRecord->FindField("ATTF");
897
898
0
    if (poATTF == nullptr)
899
0
        return;
900
901
0
    int nAttrCount = poATTF->GetRepeatCount();
902
0
    for (int iAttr = 0; iAttr < nAttrCount; iAttr++)
903
0
    {
904
0
        const int nAttrId = poRecord->GetIntSubfield("ATTF", 0, "ATTL", iAttr);
905
906
0
        if (poRegistrar->GetAttrInfo(nAttrId) == nullptr)
907
0
        {
908
0
            if (!bAttrWarningIssued)
909
0
            {
910
0
                bAttrWarningIssued = true;
911
0
                CPLError(CE_Warning, CPLE_AppDefined,
912
0
                         "Illegal feature attribute id (ATTF:ATTL[%d]) of %d\n"
913
0
                         "on feature FIDN=%d, FIDS=%d.\n"
914
0
                         "Skipping attribute. "
915
0
                         "No more warnings will be issued.",
916
0
                         iAttr, nAttrId, poFeature->GetFieldAsInteger("FIDN"),
917
0
                         poFeature->GetFieldAsInteger("FIDS"));
918
0
            }
919
920
0
            continue;
921
0
        }
922
923
        /* Fetch the attribute value */
924
0
        const char *pszValue =
925
0
            poRecord->GetStringSubfield("ATTF", 0, "ATVL", iAttr);
926
0
        if (pszValue == nullptr)
927
0
            return;
928
929
        // If needed, recode the string in UTF-8.
930
0
        char *pszValueToFree = nullptr;
931
0
        if (nOptionFlags & S57M_RECODE_BY_DSSI)
932
0
            pszValue = pszValueToFree = RecodeByDSSI(pszValue, false);
933
934
        /* Apply to feature in an appropriate way */
935
0
        const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId);
936
0
        const int iField = poFeature->GetDefnRef()->GetFieldIndex(pszAcronym);
937
0
        if (iField < 0)
938
0
        {
939
0
            if (!bMissingWarningIssued)
940
0
            {
941
0
                bMissingWarningIssued = true;
942
0
                CPLError(CE_Warning, CPLE_AppDefined,
943
0
                         "Attributes %s ignored, not in expected schema.\n"
944
0
                         "No more warnings will be issued for this dataset.",
945
0
                         pszAcronym);
946
0
            }
947
0
            CPLFree(pszValueToFree);
948
0
            continue;
949
0
        }
950
951
0
        OGRFieldDefn *poFldDefn = poFeature->GetDefnRef()->GetFieldDefn(iField);
952
0
        const auto eType = poFldDefn->GetType();
953
0
        if (eType == OFTInteger || eType == OFTReal)
954
0
        {
955
0
            if (strlen(pszValue) == 0)
956
0
            {
957
0
                if (nOptionFlags & S57M_PRESERVE_EMPTY_NUMBERS)
958
0
                    poFeature->SetField(iField, EMPTY_NUMBER_MARKER);
959
0
                else
960
0
                {
961
                    /* leave as null if value was empty string */
962
0
                }
963
0
            }
964
0
            else
965
0
                poFeature->SetField(iField, pszValue);
966
0
        }
967
0
        else if (eType == OFTStringList)
968
0
        {
969
0
            char **papszTokens = CSLTokenizeString2(pszValue, ",", 0);
970
0
            poFeature->SetField(iField, papszTokens);
971
0
            CSLDestroy(papszTokens);
972
0
        }
973
0
        else
974
0
        {
975
0
            poFeature->SetField(iField, pszValue);
976
0
        }
977
978
0
        CPLFree(pszValueToFree);
979
0
    }
980
981
    /* -------------------------------------------------------------------- */
982
    /*      NATF (national) attributes                                      */
983
    /* -------------------------------------------------------------------- */
984
0
    DDFField *poNATF = poRecord->FindField("NATF");
985
986
0
    if (poNATF == nullptr)
987
0
        return;
988
989
0
    nAttrCount = poNATF->GetRepeatCount();
990
0
    for (int iAttr = 0; iAttr < nAttrCount; iAttr++)
991
0
    {
992
0
        const int nAttrId = poRecord->GetIntSubfield("NATF", 0, "ATTL", iAttr);
993
0
        const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId);
994
995
0
        if (pszAcronym == nullptr)
996
0
        {
997
0
            if (!bAttrWarningIssued)
998
0
            {
999
0
                bAttrWarningIssued = true;
1000
0
                CPLError(CE_Warning, CPLE_AppDefined,
1001
0
                         "Illegal feature attribute id (NATF:ATTL[%d]) of %d\n"
1002
0
                         "on feature FIDN=%d, FIDS=%d.\n"
1003
0
                         "Skipping attribute, no more warnings will be issued.",
1004
0
                         iAttr, nAttrId, poFeature->GetFieldAsInteger("FIDN"),
1005
0
                         poFeature->GetFieldAsInteger("FIDS"));
1006
0
            }
1007
1008
0
            continue;
1009
0
        }
1010
1011
        // If needed, recode the string in UTF-8.
1012
0
        const char *pszValue =
1013
0
            poRecord->GetStringSubfield("NATF", 0, "ATVL", iAttr);
1014
0
        if (pszValue != nullptr)
1015
0
        {
1016
0
            if (nOptionFlags & S57M_RECODE_BY_DSSI)
1017
0
            {
1018
0
                char *pszValueRecoded = RecodeByDSSI(pszValue, true);
1019
0
                poFeature->SetField(pszAcronym, pszValueRecoded);
1020
0
                CPLFree(pszValueRecoded);
1021
0
            }
1022
0
            else
1023
0
                poFeature->SetField(pszAcronym, pszValue);
1024
0
        }
1025
0
    }
1026
0
}
1027
1028
/************************************************************************/
1029
/*                        GenerateLNAMAndRefs()                         */
1030
/************************************************************************/
1031
1032
void S57Reader::GenerateLNAMAndRefs(DDFRecord *poRecord, OGRFeature *poFeature)
1033
1034
13.1k
{
1035
    /* -------------------------------------------------------------------- */
1036
    /*      Apply the LNAM to the object.                                   */
1037
    /* -------------------------------------------------------------------- */
1038
13.1k
    char szLNAM[32];
1039
13.1k
    snprintf(szLNAM, sizeof(szLNAM), "%04X%08X%04X",
1040
13.1k
             poFeature->GetFieldAsInteger("AGEN"),
1041
13.1k
             poFeature->GetFieldAsInteger("FIDN"),
1042
13.1k
             poFeature->GetFieldAsInteger("FIDS"));
1043
13.1k
    poFeature->SetField("LNAM", szLNAM);
1044
1045
    /* -------------------------------------------------------------------- */
1046
    /*      Do we have references to other features.                        */
1047
    /* -------------------------------------------------------------------- */
1048
13.1k
    DDFField *poFFPT = poRecord->FindField("FFPT");
1049
1050
13.1k
    if (poFFPT == nullptr)
1051
13.1k
        return;
1052
1053
    /* -------------------------------------------------------------------- */
1054
    /*      Apply references.                                               */
1055
    /* -------------------------------------------------------------------- */
1056
0
    const int nRefCount = poFFPT->GetRepeatCount();
1057
1058
0
    const DDFSubfieldDefn *poLNAM =
1059
0
        poFFPT->GetFieldDefn()->FindSubfieldDefn("LNAM");
1060
0
    const DDFSubfieldDefn *poRIND =
1061
0
        poFFPT->GetFieldDefn()->FindSubfieldDefn("RIND");
1062
0
    if (poLNAM == nullptr || poRIND == nullptr)
1063
0
    {
1064
0
        return;
1065
0
    }
1066
1067
0
    int *panRIND = static_cast<int *>(CPLMalloc(sizeof(int) * nRefCount));
1068
0
    char **papszRefs = nullptr;
1069
1070
0
    for (int iRef = 0; iRef < nRefCount; iRef++)
1071
0
    {
1072
0
        int nMaxBytes = 0;
1073
1074
0
        unsigned char *pabyData =
1075
0
            reinterpret_cast<unsigned char *>(const_cast<char *>(
1076
0
                poFFPT->GetSubfieldData(poLNAM, &nMaxBytes, iRef)));
1077
0
        if (pabyData == nullptr || nMaxBytes < 8)
1078
0
        {
1079
0
            CSLDestroy(papszRefs);
1080
0
            CPLFree(panRIND);
1081
0
            return;
1082
0
        }
1083
1084
0
        snprintf(szLNAM, sizeof(szLNAM), "%02X%02X%02X%02X%02X%02X%02X%02X",
1085
0
                 pabyData[1], pabyData[0],                           /* AGEN */
1086
0
                 pabyData[5], pabyData[4], pabyData[3], pabyData[2], /* FIDN */
1087
0
                 pabyData[7], pabyData[6]);
1088
1089
0
        papszRefs = CSLAddString(papszRefs, szLNAM);
1090
1091
0
        pabyData = reinterpret_cast<unsigned char *>(const_cast<char *>(
1092
0
            poFFPT->GetSubfieldData(poRIND, &nMaxBytes, iRef)));
1093
0
        if (pabyData == nullptr || nMaxBytes < 1)
1094
0
        {
1095
0
            CSLDestroy(papszRefs);
1096
0
            CPLFree(panRIND);
1097
0
            return;
1098
0
        }
1099
0
        panRIND[iRef] = pabyData[0];
1100
0
    }
1101
1102
0
    poFeature->SetField("LNAM_REFS", papszRefs);
1103
0
    CSLDestroy(papszRefs);
1104
1105
0
    poFeature->SetField("FFPT_RIND", nRefCount, panRIND);
1106
0
    CPLFree(panRIND);
1107
0
}
1108
1109
/************************************************************************/
1110
/*                       GenerateFSPTAttributes()                       */
1111
/************************************************************************/
1112
1113
void S57Reader::GenerateFSPTAttributes(DDFRecord *poRecord,
1114
                                       OGRFeature *poFeature)
1115
1116
0
{
1117
    /* -------------------------------------------------------------------- */
1118
    /*      Feature the spatial record containing the point.                */
1119
    /* -------------------------------------------------------------------- */
1120
0
    DDFField *poFSPT = poRecord->FindField("FSPT");
1121
0
    if (poFSPT == nullptr)
1122
0
        return;
1123
1124
0
    const int nCount = poFSPT->GetRepeatCount();
1125
1126
    /* -------------------------------------------------------------------- */
1127
    /*      Allocate working lists of the attributes.                       */
1128
    /* -------------------------------------------------------------------- */
1129
0
    int *const panORNT = static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
1130
0
    int *const panUSAG = static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
1131
0
    int *const panMASK = static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
1132
0
    int *const panRCNM = static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
1133
0
    int *panRCID = static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
1134
1135
    /* -------------------------------------------------------------------- */
1136
    /*      loop over all entries, decoding them.                           */
1137
    /* -------------------------------------------------------------------- */
1138
0
    for (int i = 0; i < nCount; i++)
1139
0
    {
1140
0
        panRCID[i] = ParseName(poFSPT, i, panRCNM + i);
1141
0
        panORNT[i] = poRecord->GetIntSubfield("FSPT", 0, "ORNT", i);
1142
0
        panUSAG[i] = poRecord->GetIntSubfield("FSPT", 0, "USAG", i);
1143
0
        panMASK[i] = poRecord->GetIntSubfield("FSPT", 0, "MASK", i);
1144
0
    }
1145
1146
    /* -------------------------------------------------------------------- */
1147
    /*      Assign to feature.                                              */
1148
    /* -------------------------------------------------------------------- */
1149
0
    poFeature->SetField("NAME_RCNM", nCount, panRCNM);
1150
0
    poFeature->SetField("NAME_RCID", nCount, panRCID);
1151
0
    poFeature->SetField("ORNT", nCount, panORNT);
1152
0
    poFeature->SetField("USAG", nCount, panUSAG);
1153
0
    poFeature->SetField("MASK", nCount, panMASK);
1154
1155
    /* -------------------------------------------------------------------- */
1156
    /*      Cleanup.                                                        */
1157
    /* -------------------------------------------------------------------- */
1158
0
    CPLFree(panRCNM);
1159
0
    CPLFree(panRCID);
1160
0
    CPLFree(panORNT);
1161
0
    CPLFree(panUSAG);
1162
0
    CPLFree(panMASK);
1163
0
}
1164
1165
/************************************************************************/
1166
/*                              ReadDSID()                              */
1167
/************************************************************************/
1168
1169
OGRFeature *S57Reader::ReadDSID()
1170
1171
0
{
1172
0
    if (poDSIDRecord == nullptr && poDSPMRecord == nullptr)
1173
0
        return nullptr;
1174
1175
    /* -------------------------------------------------------------------- */
1176
    /*      Find the feature definition to use.                             */
1177
    /* -------------------------------------------------------------------- */
1178
0
    OGRFeatureDefn *poFDefn = nullptr;
1179
1180
0
    for (int i = 0; i < nFDefnCount; i++)
1181
0
    {
1182
0
        if (EQUAL(papoFDefnList[i]->GetName(), "DSID"))
1183
0
        {
1184
0
            poFDefn = papoFDefnList[i];
1185
0
            break;
1186
0
        }
1187
0
    }
1188
1189
0
    if (poFDefn == nullptr)
1190
0
    {
1191
        // CPLAssert( false );
1192
0
        return nullptr;
1193
0
    }
1194
1195
    /* -------------------------------------------------------------------- */
1196
    /*      Create feature.                                                 */
1197
    /* -------------------------------------------------------------------- */
1198
0
    OGRFeature *poFeature = new OGRFeature(poFDefn);
1199
1200
    /* -------------------------------------------------------------------- */
1201
    /*      Apply DSID values.                                              */
1202
    /* -------------------------------------------------------------------- */
1203
0
    if (poDSIDRecord != nullptr)
1204
0
    {
1205
0
        poFeature->SetField("DSID_EXPP",
1206
0
                            poDSIDRecord->GetIntSubfield("DSID", 0, "EXPP", 0));
1207
0
        poFeature->SetField("DSID_INTU",
1208
0
                            poDSIDRecord->GetIntSubfield("DSID", 0, "INTU", 0));
1209
0
        poFeature->SetField(
1210
0
            "DSID_DSNM", poDSIDRecord->GetStringSubfield("DSID", 0, "DSNM", 0));
1211
0
        if (!m_osEDTNUpdate.empty())
1212
0
            poFeature->SetField("DSID_EDTN", m_osEDTNUpdate.c_str());
1213
0
        else
1214
0
            poFeature->SetField("DSID_EDTN", poDSIDRecord->GetStringSubfield(
1215
0
                                                 "DSID", 0, "EDTN", 0));
1216
0
        if (!m_osUPDNUpdate.empty())
1217
0
            poFeature->SetField("DSID_UPDN", m_osUPDNUpdate.c_str());
1218
0
        else
1219
0
            poFeature->SetField("DSID_UPDN", poDSIDRecord->GetStringSubfield(
1220
0
                                                 "DSID", 0, "UPDN", 0));
1221
1222
0
        poFeature->SetField(
1223
0
            "DSID_UADT", poDSIDRecord->GetStringSubfield("DSID", 0, "UADT", 0));
1224
0
        if (!m_osISDTUpdate.empty())
1225
0
            poFeature->SetField("DSID_ISDT", m_osISDTUpdate.c_str());
1226
0
        else
1227
0
            poFeature->SetField("DSID_ISDT", poDSIDRecord->GetStringSubfield(
1228
0
                                                 "DSID", 0, "ISDT", 0));
1229
0
        poFeature->SetField(
1230
0
            "DSID_STED", poDSIDRecord->GetFloatSubfield("DSID", 0, "STED", 0));
1231
0
        poFeature->SetField("DSID_PRSP",
1232
0
                            poDSIDRecord->GetIntSubfield("DSID", 0, "PRSP", 0));
1233
0
        poFeature->SetField(
1234
0
            "DSID_PSDN", poDSIDRecord->GetStringSubfield("DSID", 0, "PSDN", 0));
1235
0
        poFeature->SetField(
1236
0
            "DSID_PRED", poDSIDRecord->GetStringSubfield("DSID", 0, "PRED", 0));
1237
0
        poFeature->SetField("DSID_PROF",
1238
0
                            poDSIDRecord->GetIntSubfield("DSID", 0, "PROF", 0));
1239
0
        poFeature->SetField("DSID_AGEN",
1240
0
                            poDSIDRecord->GetIntSubfield("DSID", 0, "AGEN", 0));
1241
0
        poFeature->SetField(
1242
0
            "DSID_COMT", poDSIDRecord->GetStringSubfield("DSID", 0, "COMT", 0));
1243
1244
        /* --------------------------------------------------------------------
1245
         */
1246
        /*      Apply DSSI values. */
1247
        /* --------------------------------------------------------------------
1248
         */
1249
0
        poFeature->SetField("DSSI_DSTR",
1250
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "DSTR", 0));
1251
0
        poFeature->SetField("DSSI_AALL",
1252
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "AALL", 0));
1253
0
        poFeature->SetField("DSSI_NALL",
1254
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NALL", 0));
1255
0
        poFeature->SetField("DSSI_NOMR",
1256
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOMR", 0));
1257
0
        poFeature->SetField("DSSI_NOCR",
1258
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOCR", 0));
1259
0
        poFeature->SetField("DSSI_NOGR",
1260
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOGR", 0));
1261
0
        poFeature->SetField("DSSI_NOLR",
1262
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOLR", 0));
1263
0
        poFeature->SetField("DSSI_NOIN",
1264
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOIN", 0));
1265
0
        poFeature->SetField("DSSI_NOCN",
1266
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOCN", 0));
1267
0
        poFeature->SetField("DSSI_NOED",
1268
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOED", 0));
1269
0
        poFeature->SetField("DSSI_NOFA",
1270
0
                            poDSIDRecord->GetIntSubfield("DSSI", 0, "NOFA", 0));
1271
0
    }
1272
1273
    /* -------------------------------------------------------------------- */
1274
    /*      Apply DSPM record.                                              */
1275
    /* -------------------------------------------------------------------- */
1276
0
    if (poDSPMRecord != nullptr)
1277
0
    {
1278
0
        poFeature->SetField("DSPM_HDAT",
1279
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "HDAT", 0));
1280
0
        poFeature->SetField("DSPM_VDAT",
1281
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "VDAT", 0));
1282
0
        poFeature->SetField("DSPM_SDAT",
1283
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "SDAT", 0));
1284
0
        poFeature->SetField("DSPM_CSCL",
1285
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "CSCL", 0));
1286
0
        poFeature->SetField("DSPM_DUNI",
1287
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "DUNI", 0));
1288
0
        poFeature->SetField("DSPM_HUNI",
1289
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "HUNI", 0));
1290
0
        poFeature->SetField("DSPM_PUNI",
1291
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "PUNI", 0));
1292
0
        poFeature->SetField("DSPM_COUN",
1293
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "COUN", 0));
1294
0
        poFeature->SetField("DSPM_COMF",
1295
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "COMF", 0));
1296
0
        poFeature->SetField("DSPM_SOMF",
1297
0
                            poDSPMRecord->GetIntSubfield("DSPM", 0, "SOMF", 0));
1298
0
        poFeature->SetField(
1299
0
            "DSPM_COMT", poDSPMRecord->GetStringSubfield("DSPM", 0, "COMT", 0));
1300
0
    }
1301
1302
0
    poFeature->SetFID(nNextDSIDIndex++);
1303
1304
0
    return poFeature;
1305
0
}
1306
1307
/************************************************************************/
1308
/*                             ReadVector()                             */
1309
/*                                                                      */
1310
/*      Read a vector primitive objects based on the type (RCNM_)       */
1311
/*      and index within the related index.                             */
1312
/************************************************************************/
1313
1314
OGRFeature *S57Reader::ReadVector(int nFeatureId, int nRCNM)
1315
1316
0
{
1317
0
    DDFRecordIndex *poIndex = nullptr;
1318
0
    const char *pszFDName = nullptr;
1319
1320
    /* -------------------------------------------------------------------- */
1321
    /*      What type of vector are we fetching.                            */
1322
    /* -------------------------------------------------------------------- */
1323
0
    switch (nRCNM)
1324
0
    {
1325
0
        case RCNM_VI:
1326
0
            poIndex = &oVI_Index;
1327
0
            pszFDName = OGRN_VI;
1328
0
            break;
1329
1330
0
        case RCNM_VC:
1331
0
            poIndex = &oVC_Index;
1332
0
            pszFDName = OGRN_VC;
1333
0
            break;
1334
1335
0
        case RCNM_VE:
1336
0
            poIndex = &oVE_Index;
1337
0
            pszFDName = OGRN_VE;
1338
0
            break;
1339
1340
0
        case RCNM_VF:
1341
0
            poIndex = &oVF_Index;
1342
0
            pszFDName = OGRN_VF;
1343
0
            break;
1344
1345
0
        default:
1346
0
            CPLAssert(false);
1347
0
            return nullptr;
1348
0
    }
1349
1350
0
    if (nFeatureId < 0 || nFeatureId >= poIndex->GetCount())
1351
0
        return nullptr;
1352
1353
0
    DDFRecord *poRecord = poIndex->GetByIndex(nFeatureId);
1354
1355
    /* -------------------------------------------------------------------- */
1356
    /*      Find the feature definition to use.                             */
1357
    /* -------------------------------------------------------------------- */
1358
0
    OGRFeatureDefn *poFDefn = nullptr;
1359
1360
0
    for (int i = 0; i < nFDefnCount; i++)
1361
0
    {
1362
0
        if (EQUAL(papoFDefnList[i]->GetName(), pszFDName))
1363
0
        {
1364
0
            poFDefn = papoFDefnList[i];
1365
0
            break;
1366
0
        }
1367
0
    }
1368
1369
0
    if (poFDefn == nullptr)
1370
0
    {
1371
        // CPLAssert( false );
1372
0
        return nullptr;
1373
0
    }
1374
1375
    /* -------------------------------------------------------------------- */
1376
    /*      Create feature, and assign standard fields.                     */
1377
    /* -------------------------------------------------------------------- */
1378
0
    OGRFeature *poFeature = new OGRFeature(poFDefn);
1379
1380
0
    poFeature->SetFID(nFeatureId);
1381
1382
0
    poFeature->SetField("RCNM", poRecord->GetIntSubfield("VRID", 0, "RCNM", 0));
1383
0
    poFeature->SetField("RCID", poRecord->GetIntSubfield("VRID", 0, "RCID", 0));
1384
0
    poFeature->SetField("RVER", poRecord->GetIntSubfield("VRID", 0, "RVER", 0));
1385
0
    poFeature->SetField("RUIN", poRecord->GetIntSubfield("VRID", 0, "RUIN", 0));
1386
1387
    /* -------------------------------------------------------------------- */
1388
    /*      Collect point geometries.                                       */
1389
    /* -------------------------------------------------------------------- */
1390
0
    if (nRCNM == RCNM_VI || nRCNM == RCNM_VC)
1391
0
    {
1392
0
        if (poRecord->FindField("SG2D") != nullptr)
1393
0
        {
1394
0
            const double dfX =
1395
0
                poRecord->GetIntSubfield("SG2D", 0, "XCOO", 0) / (double)nCOMF;
1396
0
            const double dfY =
1397
0
                poRecord->GetIntSubfield("SG2D", 0, "YCOO", 0) / (double)nCOMF;
1398
0
            poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY));
1399
0
        }
1400
1401
0
        else if (poRecord->FindField("SG3D") != nullptr) /* presume sounding*/
1402
0
        {
1403
0
            const int nVCount = poRecord->FindField("SG3D")->GetRepeatCount();
1404
0
            if (nVCount == 1)
1405
0
            {
1406
0
                const double dfX =
1407
0
                    poRecord->GetIntSubfield("SG3D", 0, "XCOO", 0) /
1408
0
                    (double)nCOMF;
1409
0
                const double dfY =
1410
0
                    poRecord->GetIntSubfield("SG3D", 0, "YCOO", 0) /
1411
0
                    (double)nCOMF;
1412
0
                const double dfZ =
1413
0
                    poRecord->GetIntSubfield("SG3D", 0, "VE3D", 0) /
1414
0
                    (double)nSOMF;
1415
0
                poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY, dfZ));
1416
0
            }
1417
0
            else
1418
0
            {
1419
0
                OGRMultiPoint *poMP = new OGRMultiPoint();
1420
1421
0
                for (int i = 0; i < nVCount; i++)
1422
0
                {
1423
0
                    const double dfX =
1424
0
                        poRecord->GetIntSubfield("SG3D", 0, "XCOO", i) /
1425
0
                        static_cast<double>(nCOMF);
1426
0
                    const double dfY =
1427
0
                        poRecord->GetIntSubfield("SG3D", 0, "YCOO", i) /
1428
0
                        static_cast<double>(nCOMF);
1429
0
                    const double dfZ =
1430
0
                        poRecord->GetIntSubfield("SG3D", 0, "VE3D", i) /
1431
0
                        static_cast<double>(nSOMF);
1432
1433
0
                    poMP->addGeometryDirectly(new OGRPoint(dfX, dfY, dfZ));
1434
0
                }
1435
1436
0
                poFeature->SetGeometryDirectly(poMP);
1437
0
            }
1438
0
        }
1439
0
    }
1440
1441
    /* -------------------------------------------------------------------- */
1442
    /*      Collect an edge geometry.                                       */
1443
    /* -------------------------------------------------------------------- */
1444
0
    else if (nRCNM == RCNM_VE)
1445
0
    {
1446
0
        int nPoints = 0;
1447
0
        OGRLineString *poLine = new OGRLineString();
1448
1449
0
        for (int iField = 0; iField < poRecord->GetFieldCount(); ++iField)
1450
0
        {
1451
0
            DDFField *poSG2D = poRecord->GetField(iField);
1452
1453
0
            if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D"))
1454
0
            {
1455
0
                const int nVCount = poSG2D->GetRepeatCount();
1456
1457
0
                poLine->setNumPoints(nPoints + nVCount);
1458
1459
0
                for (int i = 0; i < nVCount; ++i)
1460
0
                {
1461
0
                    poLine->setPoint(
1462
0
                        nPoints++,
1463
0
                        poRecord->GetIntSubfield("SG2D", 0, "XCOO", i) /
1464
0
                            static_cast<double>(nCOMF),
1465
0
                        poRecord->GetIntSubfield("SG2D", 0, "YCOO", i) /
1466
0
                            static_cast<double>(nCOMF));
1467
0
                }
1468
0
            }
1469
0
        }
1470
1471
0
        poFeature->SetGeometryDirectly(poLine);
1472
0
    }
1473
1474
    /* -------------------------------------------------------------------- */
1475
    /*      Special edge fields.                                            */
1476
    /*      Allow either 2 VRPT fields or one VRPT field with 2 rows        */
1477
    /* -------------------------------------------------------------------- */
1478
0
    DDFField *poVRPT = nullptr;
1479
1480
0
    if (nRCNM == RCNM_VE && (poVRPT = poRecord->FindField("VRPT")) != nullptr)
1481
0
    {
1482
0
        poFeature->SetField("NAME_RCNM_0", RCNM_VC);
1483
0
        poFeature->SetField("NAME_RCID_0", ParseName(poVRPT));
1484
0
        poFeature->SetField("ORNT_0",
1485
0
                            poRecord->GetIntSubfield("VRPT", 0, "ORNT", 0));
1486
0
        poFeature->SetField("USAG_0",
1487
0
                            poRecord->GetIntSubfield("VRPT", 0, "USAG", 0));
1488
0
        poFeature->SetField("TOPI_0",
1489
0
                            poRecord->GetIntSubfield("VRPT", 0, "TOPI", 0));
1490
0
        poFeature->SetField("MASK_0",
1491
0
                            poRecord->GetIntSubfield("VRPT", 0, "MASK", 0));
1492
1493
0
        int iField = 0;
1494
0
        int iSubField = 1;
1495
1496
0
        if (poVRPT->GetRepeatCount() == 1)
1497
0
        {
1498
            // Only one row, need a second VRPT field
1499
0
            iField = 1;
1500
0
            iSubField = 0;
1501
1502
0
            if ((poVRPT = poRecord->FindField("VRPT", iField)) == nullptr)
1503
0
            {
1504
0
                CPLError(CE_Warning, CPLE_AppDefined,
1505
0
                         "Unable to fetch last edge node.\n"
1506
0
                         "Feature OBJL=%s, RCID=%d may have corrupt or"
1507
0
                         " missing geometry.",
1508
0
                         poFeature->GetDefnRef()->GetName(),
1509
0
                         poFeature->GetFieldAsInteger("RCID"));
1510
1511
0
                return poFeature;
1512
0
            }
1513
0
        }
1514
1515
0
        poFeature->SetField("NAME_RCID_1", ParseName(poVRPT, iSubField));
1516
0
        poFeature->SetField("NAME_RCNM_1", RCNM_VC);
1517
0
        poFeature->SetField("ORNT_1", poRecord->GetIntSubfield(
1518
0
                                          "VRPT", iField, "ORNT", iSubField));
1519
0
        poFeature->SetField("USAG_1", poRecord->GetIntSubfield(
1520
0
                                          "VRPT", iField, "USAG", iSubField));
1521
0
        poFeature->SetField("TOPI_1", poRecord->GetIntSubfield(
1522
0
                                          "VRPT", iField, "TOPI", iSubField));
1523
0
        poFeature->SetField("MASK_1", poRecord->GetIntSubfield(
1524
0
                                          "VRPT", iField, "MASK", iSubField));
1525
0
    }
1526
1527
    /* -------------------------------------------------------------------- */
1528
    /*      Geometric attributes                                            */
1529
    /*      Retrieve POSACC and QUAPOS attributes                           */
1530
    /* -------------------------------------------------------------------- */
1531
1532
0
    const int posaccField = poRegistrar->FindAttrByAcronym("POSACC");
1533
0
    const int quaposField = poRegistrar->FindAttrByAcronym("QUAPOS");
1534
1535
0
    DDFField *poATTV = poRecord->FindField("ATTV");
1536
0
    if (poATTV != nullptr)
1537
0
    {
1538
0
        for (int j = 0; j < poATTV->GetRepeatCount(); j++)
1539
0
        {
1540
0
            const int subField = poRecord->GetIntSubfield("ATTV", 0, "ATTL", j);
1541
            // POSACC field
1542
0
            if (subField == posaccField)
1543
0
            {
1544
0
                poFeature->SetField(
1545
0
                    "POSACC", poRecord->GetFloatSubfield("ATTV", 0, "ATVL", j));
1546
0
            }
1547
1548
            // QUAPOS field
1549
0
            if (subField == quaposField)
1550
0
            {
1551
0
                poFeature->SetField(
1552
0
                    "QUAPOS", poRecord->GetIntSubfield("ATTV", 0, "ATVL", j));
1553
0
            }
1554
0
        }
1555
0
    }
1556
1557
0
    return poFeature;
1558
0
}
1559
1560
/************************************************************************/
1561
/*                             FetchPoint()                             */
1562
/*                                                                      */
1563
/*      Fetch the location of a spatial point object.                   */
1564
/************************************************************************/
1565
1566
bool S57Reader::FetchPoint(int nRCNM, int nRCID, double *pdfX, double *pdfY,
1567
                           double *pdfZ)
1568
1569
175
{
1570
175
    DDFRecord *poSRecord = nullptr;
1571
1572
175
    if (nRCNM == RCNM_VI)
1573
1
        poSRecord = oVI_Index.FindRecord(nRCID);
1574
174
    else
1575
174
        poSRecord = oVC_Index.FindRecord(nRCID);
1576
1577
175
    if (poSRecord == nullptr)
1578
102
        return false;
1579
1580
73
    double dfX = 0.0;
1581
73
    double dfY = 0.0;
1582
73
    double dfZ = 0.0;
1583
1584
73
    if (poSRecord->FindField("SG2D") != nullptr)
1585
72
    {
1586
72
        dfX = poSRecord->GetIntSubfield("SG2D", 0, "XCOO", 0) /
1587
72
              static_cast<double>(nCOMF);
1588
72
        dfY = poSRecord->GetIntSubfield("SG2D", 0, "YCOO", 0) /
1589
72
              static_cast<double>(nCOMF);
1590
72
    }
1591
1
    else if (poSRecord->FindField("SG3D") != nullptr)
1592
1
    {
1593
1
        dfX = poSRecord->GetIntSubfield("SG3D", 0, "XCOO", 0) /
1594
1
              static_cast<double>(nCOMF);
1595
1
        dfY = poSRecord->GetIntSubfield("SG3D", 0, "YCOO", 0) /
1596
1
              static_cast<double>(nCOMF);
1597
1
        dfZ = poSRecord->GetIntSubfield("SG3D", 0, "VE3D", 0) /
1598
1
              static_cast<double>(nSOMF);
1599
1
    }
1600
0
    else
1601
0
        return false;
1602
1603
73
    if (pdfX != nullptr)
1604
73
        *pdfX = dfX;
1605
73
    if (pdfY != nullptr)
1606
73
        *pdfY = dfY;
1607
73
    if (pdfZ != nullptr)
1608
1
        *pdfZ = dfZ;
1609
1610
73
    return true;
1611
73
}
1612
1613
/************************************************************************/
1614
/*                  S57StrokeArcToOGRGeometry_Angles()                  */
1615
/************************************************************************/
1616
1617
static OGRLineString *
1618
S57StrokeArcToOGRGeometry_Angles(double dfCenterX, double dfCenterY,
1619
                                 double dfRadius, double dfStartAngle,
1620
                                 double dfEndAngle, int nVertexCount)
1621
1622
0
{
1623
0
    OGRLineString *const poLine = new OGRLineString;
1624
1625
0
    nVertexCount = std::max(2, nVertexCount);
1626
0
    const double dfSlice = (dfEndAngle - dfStartAngle) / (nVertexCount - 1);
1627
1628
0
    poLine->setNumPoints(nVertexCount);
1629
1630
0
    for (int iPoint = 0; iPoint < nVertexCount; iPoint++)
1631
0
    {
1632
0
        const double dfAngle = (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0;
1633
1634
0
        const double dfArcX = dfCenterX + cos(dfAngle) * dfRadius;
1635
0
        const double dfArcY = dfCenterY + sin(dfAngle) * dfRadius;
1636
1637
0
        poLine->setPoint(iPoint, dfArcX, dfArcY);
1638
0
    }
1639
1640
0
    return poLine;
1641
0
}
1642
1643
/************************************************************************/
1644
/*                  S57StrokeArcToOGRGeometry_Points()                  */
1645
/************************************************************************/
1646
1647
static OGRLineString *
1648
S57StrokeArcToOGRGeometry_Points(double dfStartX, double dfStartY,
1649
                                 double dfCenterX, double dfCenterY,
1650
                                 double dfEndX, double dfEndY, int nVertexCount)
1651
1652
0
{
1653
0
    double dfStartAngle = 0.0;
1654
0
    double dfEndAngle = 360.0;
1655
1656
0
    if (dfStartX == dfEndX && dfStartY == dfEndY)
1657
0
    {
1658
        // dfStartAngle = 0.0;
1659
        // dfEndAngle = 360.0;
1660
0
    }
1661
0
    else
1662
0
    {
1663
0
        double dfDeltaX = dfStartX - dfCenterX;
1664
0
        double dfDeltaY = dfStartY - dfCenterY;
1665
0
        dfStartAngle = atan2(dfDeltaY, dfDeltaX) * 180.0 / M_PI;
1666
1667
0
        dfDeltaX = dfEndX - dfCenterX;
1668
0
        dfDeltaY = dfEndY - dfCenterY;
1669
0
        dfEndAngle = atan2(dfDeltaY, dfDeltaX) * 180.0 / M_PI;
1670
1671
#ifdef notdef
1672
        if (dfStartAngle > dfAlongAngle && dfAlongAngle > dfEndAngle)
1673
        {
1674
            // TODO: Use std::swap.
1675
            const double dfTempAngle = dfStartAngle;
1676
            dfStartAngle = dfEndAngle;
1677
            dfEndAngle = dfTempAngle;
1678
        }
1679
#endif
1680
1681
0
        while (dfStartAngle < dfEndAngle)
1682
0
            dfStartAngle += 360.0;
1683
1684
        //        while( dfAlongAngle < dfStartAngle )
1685
        //            dfAlongAngle += 360.0;
1686
1687
        //        while( dfEndAngle < dfAlongAngle )
1688
        //            dfEndAngle += 360.0;
1689
1690
0
        if (dfEndAngle - dfStartAngle > 360.0)
1691
0
        {
1692
            // TODO: Use std::swap.
1693
0
            const double dfTempAngle = dfStartAngle;
1694
0
            dfStartAngle = dfEndAngle;
1695
0
            dfEndAngle = dfTempAngle;
1696
1697
0
            while (dfEndAngle < dfStartAngle)
1698
0
                dfStartAngle -= 360.0;
1699
0
        }
1700
0
    }
1701
1702
0
    const double dfRadius =
1703
0
        sqrt((dfCenterX - dfStartX) * (dfCenterX - dfStartX) +
1704
0
             (dfCenterY - dfStartY) * (dfCenterY - dfStartY));
1705
1706
0
    return S57StrokeArcToOGRGeometry_Angles(
1707
0
        dfCenterX, dfCenterY, dfRadius, dfStartAngle, dfEndAngle, nVertexCount);
1708
0
}
1709
1710
/************************************************************************/
1711
/*                             FetchLine()                              */
1712
/************************************************************************/
1713
1714
bool S57Reader::FetchLine(DDFRecord *poSRecord, int iStartVertex,
1715
                          int iDirection, OGRLineString *poLine)
1716
1717
58
{
1718
58
    int nPoints = 0;
1719
1720
    /* -------------------------------------------------------------------- */
1721
    /*      Points may be multiple rows in one SG2D/AR2D field or           */
1722
    /*      multiple SG2D/AR2D fields (or a combination of both)            */
1723
    /*      Iterate over all the SG2D/AR2D fields in the record             */
1724
    /* -------------------------------------------------------------------- */
1725
1726
271
    for (int iField = 0; iField < poSRecord->GetFieldCount(); ++iField)
1727
213
    {
1728
213
        const DDFField *poSG2D = poSRecord->GetField(iField);
1729
213
        const DDFField *poAR2D = nullptr;
1730
1731
213
        if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D"))
1732
31
        {
1733
31
            poAR2D = nullptr;
1734
31
        }
1735
182
        else if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D"))
1736
0
        {
1737
0
            poAR2D = poSG2D;
1738
0
        }
1739
182
        else
1740
182
        {
1741
            /* Other types of fields are skipped */
1742
182
            continue;
1743
182
        }
1744
1745
        /* --------------------------------------------------------------------
1746
         */
1747
        /*      Get some basic definitions. */
1748
        /* --------------------------------------------------------------------
1749
         */
1750
1751
31
        const DDFSubfieldDefn *poXCOO =
1752
31
            poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO");
1753
31
        const DDFSubfieldDefn *poYCOO =
1754
31
            poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO");
1755
1756
31
        if (poXCOO == nullptr || poYCOO == nullptr)
1757
0
        {
1758
0
            CPLDebug("S57", "XCOO or YCOO are NULL");
1759
0
            return false;
1760
0
        }
1761
1762
31
        const int nVCount = poSG2D->GetRepeatCount();
1763
1764
        /* --------------------------------------------------------------------
1765
         */
1766
        /*      It is legitimate to have zero vertices for line segments */
1767
        /*      that just have the start and end node (bug 840). */
1768
        /*                                                                      */
1769
        /*      This is bogus! nVCount != 0, because poXCOO != 0 here */
1770
        /*      In case of zero vertices, there will not be any SG2D fields */
1771
        /* --------------------------------------------------------------------
1772
         */
1773
31
        if (nVCount == 0)
1774
0
            continue;
1775
1776
        /* --------------------------------------------------------------------
1777
         */
1778
        /*      Make sure out line is long enough to hold all the vertices */
1779
        /*      we will apply. */
1780
        /* --------------------------------------------------------------------
1781
         */
1782
31
        int nVBase = 0;
1783
1784
31
        if (iDirection < 0)
1785
0
            nVBase = iStartVertex + nPoints + nVCount;
1786
31
        else
1787
31
            nVBase = iStartVertex + nPoints;
1788
1789
31
        if (poLine->getNumPoints() < iStartVertex + nPoints + nVCount)
1790
31
            poLine->setNumPoints(iStartVertex + nPoints + nVCount);
1791
1792
31
        nPoints += nVCount;
1793
        /* --------------------------------------------------------------------
1794
         */
1795
        /*      Are the SG2D and XCOO/YCOO definitions in the form we expect? */
1796
        /* --------------------------------------------------------------------
1797
         */
1798
31
        const bool bStandardFormat =
1799
31
            (poSG2D->GetFieldDefn()->GetSubfieldCount() == 2) &&
1800
31
            EQUAL(poXCOO->GetFormat(), "b24") &&
1801
31
            EQUAL(poYCOO->GetFormat(), "b24");
1802
1803
        /* --------------------------------------------------------------------
1804
         */
1805
        /*      Collect the vertices: */
1806
        /*                                                                      */
1807
        /*      This approach assumes that the data is LSB organized int32 */
1808
        /*      binary data as per the specification.  We avoid lots of */
1809
        /*      extra calls to low level DDF methods as they are quite */
1810
        /*      expensive. */
1811
        /* --------------------------------------------------------------------
1812
         */
1813
31
        if (bStandardFormat)
1814
31
        {
1815
31
            int nBytesRemaining = 0;
1816
1817
31
            const char *pachData =
1818
31
                poSG2D->GetSubfieldData(poYCOO, &nBytesRemaining, 0);
1819
1820
2.34k
            for (int i = 0; i < nVCount; i++)
1821
2.31k
            {
1822
2.31k
                GInt32 nYCOO = 0;
1823
2.31k
                memcpy(&nYCOO, pachData, 4);
1824
2.31k
                pachData += 4;
1825
1826
2.31k
                GInt32 nXCOO = 0;
1827
2.31k
                memcpy(&nXCOO, pachData, 4);
1828
2.31k
                pachData += 4;
1829
1830
#ifdef CPL_MSB
1831
                CPL_SWAP32PTR(&nXCOO);
1832
                CPL_SWAP32PTR(&nYCOO);
1833
#endif
1834
2.31k
                const double dfX = nXCOO / static_cast<double>(nCOMF);
1835
2.31k
                const double dfY = nYCOO / static_cast<double>(nCOMF);
1836
1837
2.31k
                poLine->setPoint(nVBase, dfX, dfY);
1838
1839
2.31k
                nVBase += iDirection;
1840
2.31k
            }
1841
31
        }
1842
1843
        /* --------------------------------------------------------------------
1844
         */
1845
        /*      Collect the vertices: */
1846
        /*                                                                      */
1847
        /*      The generic case where we use low level but expensive DDF */
1848
        /*      methods to get the data.  This should work even if some */
1849
        /*      things are changed about the SG2D fields such as making them */
1850
        /*      floating point or a different byte order. */
1851
        /* --------------------------------------------------------------------
1852
         */
1853
0
        else
1854
0
        {
1855
0
            for (int i = 0; i < nVCount; i++)
1856
0
            {
1857
0
                int nBytesRemaining = 0;
1858
1859
0
                const char *pachData =
1860
0
                    poSG2D->GetSubfieldData(poXCOO, &nBytesRemaining, i);
1861
1862
0
                const double dfX =
1863
0
                    poXCOO->ExtractIntData(pachData, nBytesRemaining, nullptr) /
1864
0
                    static_cast<double>(nCOMF);
1865
1866
0
                pachData = poSG2D->GetSubfieldData(poYCOO, &nBytesRemaining, i);
1867
1868
0
                const double dfY =
1869
0
                    poXCOO->ExtractIntData(pachData, nBytesRemaining, nullptr) /
1870
0
                    static_cast<double>(nCOMF);
1871
1872
0
                poLine->setPoint(nVBase, dfX, dfY);
1873
1874
0
                nVBase += iDirection;
1875
0
            }
1876
0
        }
1877
1878
        /* --------------------------------------------------------------------
1879
         */
1880
        /*      If this is actually an arc, turn the start, end and center */
1881
        /*      of rotation into a "stroked" arc linestring. */
1882
        /* --------------------------------------------------------------------
1883
         */
1884
31
        if (poAR2D != nullptr && poLine->getNumPoints() >= 3)
1885
0
        {
1886
0
            int iLast = poLine->getNumPoints() - 1;
1887
1888
0
            OGRLineString *poArc = S57StrokeArcToOGRGeometry_Points(
1889
0
                poLine->getX(iLast - 0), poLine->getY(iLast - 0),
1890
0
                poLine->getX(iLast - 1), poLine->getY(iLast - 1),
1891
0
                poLine->getX(iLast - 2), poLine->getY(iLast - 2), 30);
1892
1893
0
            if (poArc != nullptr)
1894
0
            {
1895
0
                for (int i = 0; i < poArc->getNumPoints(); i++)
1896
0
                    poLine->setPoint(iLast - 2 + i, poArc->getX(i),
1897
0
                                     poArc->getY(i));
1898
1899
0
                delete poArc;
1900
0
            }
1901
0
        }
1902
31
    }
1903
1904
58
    return true;
1905
58
}
1906
1907
/************************************************************************/
1908
/*                       AssemblePointGeometry()                        */
1909
/************************************************************************/
1910
1911
void S57Reader::AssemblePointGeometry(DDFRecord *poFRecord,
1912
                                      OGRFeature *poFeature)
1913
1914
3.13k
{
1915
    /* -------------------------------------------------------------------- */
1916
    /*      Feature the spatial record containing the point.                */
1917
    /* -------------------------------------------------------------------- */
1918
3.13k
    DDFField *poFSPT = poFRecord->FindField("FSPT");
1919
3.13k
    if (poFSPT == nullptr)
1920
3.11k
        return;
1921
1922
28
    if (poFSPT->GetRepeatCount() != 1)
1923
2
    {
1924
#ifdef DEBUG
1925
        fprintf(stderr, /*ok*/
1926
                "Point features with other than one spatial linkage.\n");
1927
        poFRecord->Dump(stderr);
1928
#endif
1929
2
        CPLDebug(
1930
2
            "S57",
1931
2
            "Point feature encountered with other than one spatial linkage.");
1932
2
    }
1933
1934
28
    int nRCNM = 0;
1935
28
    const int nRCID = ParseName(poFSPT, 0, &nRCNM);
1936
1937
28
    double dfX = 0.0;
1938
28
    double dfY = 0.0;
1939
28
    double dfZ = 0.0;
1940
1941
28
    if (nRCID == -1 || !FetchPoint(nRCNM, nRCID, &dfX, &dfY, &dfZ))
1942
27
    {
1943
27
        CPLError(CE_Warning, CPLE_AppDefined,
1944
27
                 "Failed to fetch %d/%d point geometry for point feature.\n"
1945
27
                 "Feature will have empty geometry.",
1946
27
                 nRCNM, nRCID);
1947
27
        return;
1948
27
    }
1949
1950
1
    if (dfZ == 0.0)
1951
0
        poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY));
1952
1
    else
1953
1
        poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY, dfZ));
1954
1
}
1955
1956
/************************************************************************/
1957
/*                      AssembleSoundingGeometry()                      */
1958
/************************************************************************/
1959
1960
void S57Reader::AssembleSoundingGeometry(DDFRecord *poFRecord,
1961
                                         OGRFeature *poFeature)
1962
1963
56
{
1964
    /* -------------------------------------------------------------------- */
1965
    /*      Feature the spatial record containing the point.                */
1966
    /* -------------------------------------------------------------------- */
1967
56
    DDFField *poFSPT = poFRecord->FindField("FSPT");
1968
56
    if (poFSPT == nullptr)
1969
27
        return;
1970
1971
29
    if (poFSPT->GetRepeatCount() != 1)
1972
1
        return;
1973
1974
28
    int nRCNM = 0;
1975
28
    const int nRCID = ParseName(poFSPT, 0, &nRCNM);
1976
1977
28
    DDFRecord *poSRecord = nRCNM == RCNM_VI ? oVI_Index.FindRecord(nRCID)
1978
28
                                            : oVC_Index.FindRecord(nRCID);
1979
1980
28
    if (poSRecord == nullptr)
1981
19
        return;
1982
1983
    /* -------------------------------------------------------------------- */
1984
    /*      Extract vertices.                                               */
1985
    /* -------------------------------------------------------------------- */
1986
9
    OGRMultiPoint *const poMP = new OGRMultiPoint();
1987
1988
9
    DDFField *poField = poSRecord->FindField("SG2D");
1989
9
    if (poField == nullptr)
1990
9
        poField = poSRecord->FindField("SG3D");
1991
9
    if (poField == nullptr)
1992
0
    {
1993
0
        delete poMP;
1994
0
        return;
1995
0
    }
1996
1997
9
    const DDFSubfieldDefn *poXCOO =
1998
9
        poField->GetFieldDefn()->FindSubfieldDefn("XCOO");
1999
9
    const DDFSubfieldDefn *poYCOO =
2000
9
        poField->GetFieldDefn()->FindSubfieldDefn("YCOO");
2001
9
    if (poXCOO == nullptr || poYCOO == nullptr)
2002
0
    {
2003
0
        CPLDebug("S57", "XCOO or YCOO are NULL");
2004
0
        delete poMP;
2005
0
        return;
2006
0
    }
2007
9
    const DDFSubfieldDefn *const poVE3D =
2008
9
        poField->GetFieldDefn()->FindSubfieldDefn("VE3D");
2009
2010
9
    const int nPointCount = poField->GetRepeatCount();
2011
2012
9
    const char *pachData = poField->GetData();
2013
9
    int nBytesLeft = poField->GetDataSize();
2014
2015
18
    for (int i = 0; i < nPointCount; i++)
2016
9
    {
2017
9
        int nBytesConsumed = 0;
2018
2019
9
        const double dfY =
2020
9
            poYCOO->ExtractIntData(pachData, nBytesLeft, &nBytesConsumed) /
2021
9
            static_cast<double>(nCOMF);
2022
9
        nBytesLeft -= nBytesConsumed;
2023
9
        pachData += nBytesConsumed;
2024
2025
9
        const double dfX =
2026
9
            poXCOO->ExtractIntData(pachData, nBytesLeft, &nBytesConsumed) /
2027
9
            static_cast<double>(nCOMF);
2028
9
        nBytesLeft -= nBytesConsumed;
2029
9
        pachData += nBytesConsumed;
2030
2031
9
        double dfZ = 0.0;
2032
9
        if (poVE3D != nullptr)
2033
9
        {
2034
9
            dfZ =
2035
9
                poYCOO->ExtractIntData(pachData, nBytesLeft, &nBytesConsumed) /
2036
9
                static_cast<double>(nSOMF);
2037
9
            nBytesLeft -= nBytesConsumed;
2038
9
            pachData += nBytesConsumed;
2039
9
        }
2040
2041
9
        poMP->addGeometryDirectly(new OGRPoint(dfX, dfY, dfZ));
2042
9
    }
2043
2044
9
    poFeature->SetGeometryDirectly(poMP);
2045
9
}
2046
2047
/************************************************************************/
2048
/*                            GetIntSubfield()                          */
2049
/************************************************************************/
2050
2051
static int GetIntSubfield(const DDFField *poField, const char *pszSubfield,
2052
                          int iSubfieldIndex)
2053
565
{
2054
565
    const DDFSubfieldDefn *poSFDefn =
2055
565
        poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
2056
2057
565
    if (poSFDefn == nullptr)
2058
499
        return 0;
2059
2060
    /* -------------------------------------------------------------------- */
2061
    /*      Get a pointer to the data.                                      */
2062
    /* -------------------------------------------------------------------- */
2063
66
    int nBytesRemaining = 0;
2064
2065
66
    const char *pachData =
2066
66
        poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
2067
2068
66
    return poSFDefn->ExtractIntData(pachData, nBytesRemaining, nullptr);
2069
565
}
2070
2071
/************************************************************************/
2072
/*                        AssembleLineGeometry()                        */
2073
/************************************************************************/
2074
2075
void S57Reader::AssembleLineGeometry(DDFRecord *poFRecord,
2076
                                     OGRFeature *poFeature)
2077
2078
3.88k
{
2079
3.88k
    OGRLineString *poLine = new OGRLineString();
2080
3.88k
    OGRMultiLineString *poMLS = new OGRMultiLineString();
2081
2082
    /* -------------------------------------------------------------------- */
2083
    /*      Loop collecting edges.                                          */
2084
    /*      Iterate over the FSPT fields.                                   */
2085
    /* -------------------------------------------------------------------- */
2086
3.88k
    const int nFieldCount = poFRecord->GetFieldCount();
2087
2088
11.8k
    for (int iField = 0; iField < nFieldCount; ++iField)
2089
7.99k
    {
2090
7.99k
        double dlastfX = 0.0;
2091
7.99k
        double dlastfY = 0.0;
2092
2093
7.99k
        const DDFField *poFSPT = poFRecord->GetField(iField);
2094
2095
7.99k
        const auto poFieldDefn = poFSPT->GetFieldDefn();
2096
7.99k
        if (!poFieldDefn || !EQUAL(poFieldDefn->GetName(), "FSPT"))
2097
7.93k
            continue;
2098
2099
        /* --------------------------------------------------------------------
2100
         */
2101
        /*      Loop over the rows of each FSPT field */
2102
        /* --------------------------------------------------------------------
2103
         */
2104
59
        const int nEdgeCount = poFSPT->GetRepeatCount();
2105
2106
141
        for (int iEdge = 0; iEdge < nEdgeCount; ++iEdge)
2107
82
        {
2108
82
            const bool bReverse = (GetIntSubfield(poFSPT, "ORNT", iEdge) == 2);
2109
2110
            /* --------------------------------------------------------------------
2111
             */
2112
            /*      Find the spatial record for this edge. */
2113
            /* --------------------------------------------------------------------
2114
             */
2115
82
            const int nRCID = ParseName(poFSPT, iEdge);
2116
2117
82
            DDFRecord *poSRecord = oVE_Index.FindRecord(nRCID);
2118
82
            if (poSRecord == nullptr)
2119
60
            {
2120
60
                CPLError(CE_Warning, CPLE_AppDefined,
2121
60
                         "Couldn't find spatial record %d.\n"
2122
60
                         "Feature OBJL=%s, RCID=%d may have corrupt or"
2123
60
                         "missing geometry.",
2124
60
                         nRCID, poFeature->GetDefnRef()->GetName(),
2125
60
                         GetIntSubfield(poFSPT, "RCID", 0));
2126
60
                continue;
2127
60
            }
2128
2129
            /* --------------------------------------------------------------------
2130
             */
2131
            /*      Get the first and last nodes */
2132
            /* --------------------------------------------------------------------
2133
             */
2134
22
            DDFField *poVRPT = poSRecord->FindField("VRPT");
2135
22
            if (poVRPT == nullptr)
2136
0
            {
2137
0
                CPLError(CE_Warning, CPLE_AppDefined,
2138
0
                         "Unable to fetch start node for RCID %d.\n"
2139
0
                         "Feature OBJL=%s, RCID=%d may have corrupt or"
2140
0
                         "missing geometry.",
2141
0
                         nRCID, poFeature->GetDefnRef()->GetName(),
2142
0
                         GetIntSubfield(poFSPT, "RCID", 0));
2143
0
                continue;
2144
0
            }
2145
2146
            // The "VRPT" field has only one row
2147
            // Get the next row from a second "VRPT" field
2148
22
            int nVC_RCID_firstnode = 0;
2149
22
            int nVC_RCID_lastnode = 0;
2150
2151
22
            if (poVRPT->GetRepeatCount() == 1)
2152
0
            {
2153
0
                nVC_RCID_firstnode = ParseName(poVRPT);
2154
0
                poVRPT = poSRecord->FindField("VRPT", 1);
2155
2156
0
                if (poVRPT == nullptr)
2157
0
                {
2158
0
                    CPLError(CE_Warning, CPLE_AppDefined,
2159
0
                             "Unable to fetch end node for RCID %d.\n"
2160
0
                             "Feature OBJL=%s, RCID=%d may have corrupt or"
2161
0
                             "missing geometry.",
2162
0
                             nRCID, poFeature->GetDefnRef()->GetName(),
2163
0
                             GetIntSubfield(poFSPT, "RCID", 0));
2164
0
                    continue;
2165
0
                }
2166
2167
0
                nVC_RCID_lastnode = ParseName(poVRPT);
2168
2169
0
                if (bReverse)
2170
0
                {
2171
                    // TODO: std::swap.
2172
0
                    const int tmp = nVC_RCID_lastnode;
2173
0
                    nVC_RCID_lastnode = nVC_RCID_firstnode;
2174
0
                    nVC_RCID_firstnode = tmp;
2175
0
                }
2176
0
            }
2177
22
            else if (bReverse)
2178
0
            {
2179
0
                nVC_RCID_lastnode = ParseName(poVRPT);
2180
0
                nVC_RCID_firstnode = ParseName(poVRPT, 1);
2181
0
            }
2182
22
            else
2183
22
            {
2184
22
                nVC_RCID_firstnode = ParseName(poVRPT);
2185
22
                nVC_RCID_lastnode = ParseName(poVRPT, 1);
2186
22
            }
2187
2188
22
            double dfX = 0.0;
2189
22
            double dfY = 0.0;
2190
22
            if (nVC_RCID_firstnode == -1 ||
2191
22
                !FetchPoint(RCNM_VC, nVC_RCID_firstnode, &dfX, &dfY))
2192
7
            {
2193
7
                CPLError(CE_Warning, CPLE_AppDefined,
2194
7
                         "Unable to fetch start node RCID=%d.\n"
2195
7
                         "Feature OBJL=%s, RCID=%d may have corrupt or"
2196
7
                         " missing geometry.",
2197
7
                         nVC_RCID_firstnode, poFeature->GetDefnRef()->GetName(),
2198
7
                         poFRecord->GetIntSubfield("FRID", 0, "RCID", 0));
2199
2200
7
                continue;
2201
7
            }
2202
2203
            /* --------------------------------------------------------------------
2204
             */
2205
            /*      Does the first node match the trailing node on the existing
2206
             */
2207
            /*      line string?  If so, skip it, otherwise if the existing */
2208
            /*      linestring is not empty we need to push it out and start a
2209
             */
2210
            /*      new one as it means things are not connected. */
2211
            /* --------------------------------------------------------------------
2212
             */
2213
15
            if (poLine->getNumPoints() == 0)
2214
6
            {
2215
6
                poLine->addPoint(dfX, dfY);
2216
6
            }
2217
9
            else if (std::abs(dlastfX - dfX) > 0.00000001 ||
2218
9
                     std::abs(dlastfY - dfY) > 0.00000001)
2219
0
            {
2220
                // we need to start a new linestring.
2221
0
                poMLS->addGeometryDirectly(poLine);
2222
0
                poLine = new OGRLineString();
2223
0
                poLine->addPoint(dfX, dfY);
2224
0
            }
2225
9
            else
2226
9
            {
2227
                /* omit point, already present */
2228
9
            }
2229
2230
            /* --------------------------------------------------------------------
2231
             */
2232
            /*      Collect the vertices. */
2233
            /*      Iterate over all the SG2D fields in the Spatial record */
2234
            /* --------------------------------------------------------------------
2235
             */
2236
75
            for (int iSField = 0; iSField < poSRecord->GetFieldCount();
2237
60
                 ++iSField)
2238
60
            {
2239
60
                const DDFField *poSG2D = poSRecord->GetField(iSField);
2240
2241
60
                if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") ||
2242
60
                    EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D"))
2243
15
                {
2244
15
                    const DDFSubfieldDefn *poXCOO =
2245
15
                        poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO");
2246
15
                    const DDFSubfieldDefn *poYCOO =
2247
15
                        poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO");
2248
2249
15
                    if (poXCOO == nullptr || poYCOO == nullptr)
2250
0
                    {
2251
0
                        CPLDebug("S57", "XCOO or YCOO are NULL");
2252
0
                        delete poLine;
2253
0
                        delete poMLS;
2254
0
                        return;
2255
0
                    }
2256
2257
15
                    const int nVCount = poSG2D->GetRepeatCount();
2258
2259
15
                    int nStart = 0;
2260
15
                    int nEnd = 0;
2261
15
                    int nInc = 0;
2262
15
                    if (bReverse)
2263
0
                    {
2264
0
                        nStart = nVCount - 1;
2265
0
                        nInc = -1;
2266
0
                    }
2267
15
                    else
2268
15
                    {
2269
15
                        nEnd = nVCount - 1;
2270
15
                        nInc = 1;
2271
15
                    }
2272
2273
15
                    int nVBase = poLine->getNumPoints();
2274
15
                    poLine->setNumPoints(nVBase + nVCount);
2275
2276
15
                    int nBytesRemaining = 0;
2277
2278
1.63k
                    for (int i = nStart; i != nEnd + nInc; i += nInc)
2279
1.62k
                    {
2280
1.62k
                        const char *pachData = poSG2D->GetSubfieldData(
2281
1.62k
                            poXCOO, &nBytesRemaining, i);
2282
2283
1.62k
                        dfX = poXCOO->ExtractIntData(pachData, nBytesRemaining,
2284
1.62k
                                                     nullptr) /
2285
1.62k
                              static_cast<double>(nCOMF);
2286
2287
1.62k
                        pachData = poSG2D->GetSubfieldData(poYCOO,
2288
1.62k
                                                           &nBytesRemaining, i);
2289
2290
1.62k
                        dfY = poXCOO->ExtractIntData(pachData, nBytesRemaining,
2291
1.62k
                                                     nullptr) /
2292
1.62k
                              static_cast<double>(nCOMF);
2293
2294
1.62k
                        poLine->setPoint(nVBase++, dfX, dfY);
2295
1.62k
                    }
2296
15
                }
2297
60
            }
2298
2299
            // remember the coordinates of the last point
2300
15
            dlastfX = dfX;
2301
15
            dlastfY = dfY;
2302
2303
            /* --------------------------------------------------------------------
2304
             */
2305
            /*      Add the end node. */
2306
            /* --------------------------------------------------------------------
2307
             */
2308
15
            if (nVC_RCID_lastnode != -1 &&
2309
15
                FetchPoint(RCNM_VC, nVC_RCID_lastnode, &dfX, &dfY))
2310
15
            {
2311
15
                poLine->addPoint(dfX, dfY);
2312
15
                dlastfX = dfX;
2313
15
                dlastfY = dfY;
2314
15
            }
2315
0
            else
2316
0
            {
2317
0
                CPLError(CE_Warning, CPLE_AppDefined,
2318
0
                         "Unable to fetch end node RCID=%d.\n"
2319
0
                         "Feature OBJL=%s, RCID=%d may have corrupt or"
2320
0
                         " missing geometry.",
2321
0
                         nVC_RCID_lastnode, poFeature->GetDefnRef()->GetName(),
2322
0
                         poFRecord->GetIntSubfield("FRID", 0, "RCID", 0));
2323
0
                continue;
2324
0
            }
2325
15
        }
2326
59
    }
2327
2328
    /* -------------------------------------------------------------------- */
2329
    /*      Set either the line or multilinestring as the geometry.  We     */
2330
    /*      are careful to just produce a linestring if there are no        */
2331
    /*      disconnections.                                                 */
2332
    /* -------------------------------------------------------------------- */
2333
3.88k
    if (poMLS->getNumGeometries() > 0)
2334
0
    {
2335
0
        poMLS->addGeometryDirectly(poLine);
2336
0
        poFeature->SetGeometryDirectly(poMLS);
2337
0
    }
2338
3.88k
    else if (poLine->getNumPoints() >= 2)
2339
6
    {
2340
6
        poFeature->SetGeometryDirectly(poLine);
2341
6
        delete poMLS;
2342
6
    }
2343
3.88k
    else
2344
3.88k
    {
2345
3.88k
        delete poLine;
2346
3.88k
        delete poMLS;
2347
3.88k
    }
2348
3.88k
}
2349
2350
/************************************************************************/
2351
/*                        AssembleAreaGeometry()                        */
2352
/************************************************************************/
2353
2354
void S57Reader::AssembleAreaGeometry(const DDFRecord *poFRecord,
2355
                                     OGRFeature *poFeature)
2356
2357
6.05k
{
2358
6.05k
    OGRGeometryCollection *const poLines = new OGRGeometryCollection();
2359
2360
    /* -------------------------------------------------------------------- */
2361
    /*      Find the FSPT fields.                                           */
2362
    /* -------------------------------------------------------------------- */
2363
6.05k
    const int nFieldCount = poFRecord->GetFieldCount();
2364
2365
18.4k
    for (int iFSPT = 0; iFSPT < nFieldCount; ++iFSPT)
2366
12.3k
    {
2367
12.3k
        const DDFField *poFSPT = poFRecord->GetField(iFSPT);
2368
2369
12.3k
        const auto poFieldDefn = poFSPT->GetFieldDefn();
2370
12.3k
        if (!poFieldDefn || !EQUAL(poFieldDefn->GetName(), "FSPT"))
2371
12.3k
            continue;
2372
2373
74
        const int nEdgeCount = poFSPT->GetRepeatCount();
2374
2375
        /* ====================================================================
2376
         */
2377
        /*      Loop collecting edges. */
2378
        /* ====================================================================
2379
         */
2380
555
        for (int iEdge = 0; iEdge < nEdgeCount; iEdge++)
2381
481
        {
2382
            /* --------------------------------------------------------------------
2383
             */
2384
            /*      Find the spatial record for this edge. */
2385
            /* --------------------------------------------------------------------
2386
             */
2387
481
            const int nRCID = ParseName(poFSPT, iEdge);
2388
2389
481
            DDFRecord *poSRecord = oVE_Index.FindRecord(nRCID);
2390
481
            if (poSRecord == nullptr)
2391
423
            {
2392
423
                CPLError(CE_Warning, CPLE_AppDefined,
2393
423
                         "Couldn't find spatial record %d.\n"
2394
423
                         "Feature OBJL=%s, RCID=%d may have corrupt or"
2395
423
                         "missing geometry.",
2396
423
                         nRCID, poFeature->GetDefnRef()->GetName(),
2397
423
                         GetIntSubfield(poFSPT, "RCID", 0));
2398
423
                continue;
2399
423
            }
2400
2401
            /* --------------------------------------------------------------------
2402
             */
2403
            /*      Create the line string. */
2404
            /* --------------------------------------------------------------------
2405
             */
2406
58
            OGRLineString *poLine = new OGRLineString();
2407
2408
            /* --------------------------------------------------------------------
2409
             */
2410
            /*      Add the start node. */
2411
            /* --------------------------------------------------------------------
2412
             */
2413
58
            DDFField *poVRPT = poSRecord->FindField("VRPT");
2414
58
            if (poVRPT != nullptr)
2415
58
            {
2416
58
                int nVC_RCID = ParseName(poVRPT);
2417
58
                double dfX = 0.0;
2418
58
                double dfY = 0.0;
2419
2420
58
                if (nVC_RCID != -1 && FetchPoint(RCNM_VC, nVC_RCID, &dfX, &dfY))
2421
21
                    poLine->addPoint(dfX, dfY);
2422
58
            }
2423
2424
            /* --------------------------------------------------------------------
2425
             */
2426
            /*      Collect the vertices. */
2427
            /* --------------------------------------------------------------------
2428
             */
2429
58
            if (!FetchLine(poSRecord, poLine->getNumPoints(), 1, poLine))
2430
0
            {
2431
0
                CPLDebug("S57",
2432
0
                         "FetchLine() failed in AssembleAreaGeometry()!");
2433
0
            }
2434
2435
            /* --------------------------------------------------------------------
2436
             */
2437
            /*      Add the end node. */
2438
            /* --------------------------------------------------------------------
2439
             */
2440
58
            if (poVRPT != nullptr && poVRPT->GetRepeatCount() > 1)
2441
55
            {
2442
55
                const int nVC_RCID = ParseName(poVRPT, 1);
2443
55
                double dfX = 0.0;
2444
55
                double dfY = 0.0;
2445
2446
55
                if (nVC_RCID != -1 && FetchPoint(RCNM_VC, nVC_RCID, &dfX, &dfY))
2447
21
                    poLine->addPoint(dfX, dfY);
2448
55
            }
2449
3
            else if ((poVRPT = poSRecord->FindField("VRPT", 1)) != nullptr)
2450
0
            {
2451
0
                const int nVC_RCID = ParseName(poVRPT);
2452
0
                double dfX = 0.0;
2453
0
                double dfY = 0.0;
2454
2455
0
                if (nVC_RCID != -1 && FetchPoint(RCNM_VC, nVC_RCID, &dfX, &dfY))
2456
0
                    poLine->addPoint(dfX, dfY);
2457
0
            }
2458
2459
58
            poLines->addGeometryDirectly(poLine);
2460
58
        }
2461
74
    }
2462
2463
    /* -------------------------------------------------------------------- */
2464
    /*      Build lines into a polygon.                                     */
2465
    /* -------------------------------------------------------------------- */
2466
6.05k
    OGRErr eErr;
2467
2468
6.05k
    OGRGeometry *poPolygon = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
2469
6.05k
        OGRGeometry::ToHandle(poLines), TRUE, FALSE, 0.0, &eErr));
2470
6.05k
    if (eErr != OGRERR_NONE)
2471
13
    {
2472
13
        CPLError(CE_Warning, CPLE_AppDefined,
2473
13
                 "Polygon assembly has failed for feature FIDN=%d,FIDS=%d.\n"
2474
13
                 "Geometry may be missing or incomplete.",
2475
13
                 poFeature->GetFieldAsInteger("FIDN"),
2476
13
                 poFeature->GetFieldAsInteger("FIDS"));
2477
13
    }
2478
2479
6.05k
    delete poLines;
2480
2481
6.05k
    if (poPolygon != nullptr)
2482
6.05k
        poFeature->SetGeometryDirectly(poPolygon);
2483
6.05k
}
2484
2485
/************************************************************************/
2486
/*                             FindFDefn()                              */
2487
/*                                                                      */
2488
/*      Find the OGRFeatureDefn corresponding to the passed feature     */
2489
/*      record.  It will search based on geometry class, or object      */
2490
/*      class depending on the bClassBased setting.                     */
2491
/************************************************************************/
2492
2493
OGRFeatureDefn *S57Reader::FindFDefn(DDFRecord *poRecord)
2494
2495
708k
{
2496
708k
    if (poRegistrar != nullptr)
2497
0
    {
2498
0
        const int nOBJL = poRecord->GetIntSubfield("FRID", 0, "OBJL", 0);
2499
2500
0
        if (nOBJL < static_cast<int>(apoFDefnByOBJL.size()) &&
2501
0
            apoFDefnByOBJL[nOBJL] != nullptr)
2502
0
            return apoFDefnByOBJL[nOBJL];
2503
2504
0
        if (!poClassContentExplorer->SelectClass(nOBJL))
2505
0
        {
2506
0
            for (int i = 0; i < nFDefnCount; i++)
2507
0
            {
2508
0
                if (EQUAL(papoFDefnList[i]->GetName(), "Generic"))
2509
0
                    return papoFDefnList[i];
2510
0
            }
2511
0
            return nullptr;
2512
0
        }
2513
2514
0
        for (int i = 0; i < nFDefnCount; i++)
2515
0
        {
2516
0
            const char *pszAcronym = poClassContentExplorer->GetAcronym();
2517
0
            if (pszAcronym != nullptr &&
2518
0
                EQUAL(papoFDefnList[i]->GetName(), pszAcronym))
2519
0
                return papoFDefnList[i];
2520
0
        }
2521
2522
0
        return nullptr;
2523
0
    }
2524
708k
    else
2525
708k
    {
2526
708k
        const int nPRIM = poRecord->GetIntSubfield("FRID", 0, "PRIM", 0);
2527
708k
        OGRwkbGeometryType eGType;
2528
2529
708k
        if (nPRIM == PRIM_P)
2530
11.0k
            eGType = wkbPoint;
2531
696k
        else if (nPRIM == PRIM_L)
2532
7.87k
            eGType = wkbLineString;
2533
689k
        else if (nPRIM == PRIM_A)
2534
12.1k
            eGType = wkbPolygon;
2535
676k
        else
2536
676k
            eGType = wkbNone;
2537
2538
771k
        for (int i = 0; i < nFDefnCount; i++)
2539
771k
        {
2540
771k
            if (papoFDefnList[i]->GetGeomType() == eGType)
2541
708k
                return papoFDefnList[i];
2542
771k
        }
2543
708k
    }
2544
2545
0
    return nullptr;
2546
708k
}
2547
2548
/************************************************************************/
2549
/*                             ParseName()                              */
2550
/*                                                                      */
2551
/*      Pull the RCNM and RCID values from a NAME field.  The RCID      */
2552
/*      is returned and the RCNM can be gotten via the pnRCNM argument. */
2553
/*      Note: nIndex is the index of the requested 'NAME' instance      */
2554
/************************************************************************/
2555
2556
int S57Reader::ParseName(const DDFField *poField, int nIndex, int *pnRCNM)
2557
2558
776
{
2559
776
    if (poField == nullptr)
2560
0
    {
2561
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing field in ParseName().");
2562
0
        return -1;
2563
0
    }
2564
2565
776
    const DDFSubfieldDefn *poName =
2566
776
        poField->GetFieldDefn()->FindSubfieldDefn("NAME");
2567
776
    if (poName == nullptr)
2568
42
        return -1;
2569
2570
734
    int nMaxBytes = 0;
2571
734
    unsigned char *pabyData =
2572
734
        reinterpret_cast<unsigned char *>(const_cast<char *>(
2573
734
            poField->GetSubfieldData(poName, &nMaxBytes, nIndex)));
2574
734
    if (pabyData == nullptr || nMaxBytes < 5)
2575
1
        return -1;
2576
2577
733
    if (pnRCNM != nullptr)
2578
54
        *pnRCNM = pabyData[0];
2579
2580
733
    return CPL_LSBSINT32PTR(pabyData + 1);
2581
734
}
2582
2583
/************************************************************************/
2584
/*                           AddFeatureDefn()                           */
2585
/************************************************************************/
2586
2587
void S57Reader::AddFeatureDefn(OGRFeatureDefn *poFDefn)
2588
2589
2.14k
{
2590
2.14k
    nFDefnCount++;
2591
2.14k
    papoFDefnList = static_cast<OGRFeatureDefn **>(
2592
2.14k
        CPLRealloc(papoFDefnList, sizeof(OGRFeatureDefn *) * nFDefnCount));
2593
2594
2.14k
    papoFDefnList[nFDefnCount - 1] = poFDefn;
2595
2596
2.14k
    if (poRegistrar != nullptr)
2597
0
    {
2598
0
        if (poClassContentExplorer->SelectClass(poFDefn->GetName()))
2599
0
        {
2600
0
            const int nOBJL = poClassContentExplorer->GetOBJL();
2601
0
            if (nOBJL >= 0)
2602
0
            {
2603
0
                if (nOBJL >= (int)apoFDefnByOBJL.size())
2604
0
                    apoFDefnByOBJL.resize(nOBJL + 1);
2605
0
                apoFDefnByOBJL[nOBJL] = poFDefn;
2606
0
            }
2607
0
        }
2608
0
    }
2609
2.14k
}
2610
2611
/************************************************************************/
2612
/*                          CollectClassList()                          */
2613
/*                                                                      */
2614
/*      Establish the list of classes (unique OBJL values) that         */
2615
/*      occur in this dataset.                                          */
2616
/************************************************************************/
2617
2618
bool S57Reader::CollectClassList(std::vector<int> &anClassCount)
2619
2620
0
{
2621
0
    if (!bFileIngested && !Ingest())
2622
0
        return false;
2623
2624
0
    bool bSuccess = true;
2625
2626
0
    for (int iFEIndex = 0; iFEIndex < oFE_Index.GetCount(); iFEIndex++)
2627
0
    {
2628
0
        DDFRecord *poRecord = oFE_Index.GetByIndex(iFEIndex);
2629
0
        const int nOBJL = poRecord->GetIntSubfield("FRID", 0, "OBJL", 0);
2630
2631
0
        if (nOBJL < 0)
2632
0
            bSuccess = false;
2633
0
        else
2634
0
        {
2635
0
            if (nOBJL >= (int)anClassCount.size())
2636
0
                anClassCount.resize(nOBJL + 1);
2637
0
            anClassCount[nOBJL]++;
2638
0
        }
2639
0
    }
2640
2641
0
    return bSuccess;
2642
0
}
2643
2644
/************************************************************************/
2645
/*                         ApplyRecordUpdate()                          */
2646
/*                                                                      */
2647
/*      Update one target record based on an S-57 update record         */
2648
/*      (RUIN=3).                                                       */
2649
/************************************************************************/
2650
2651
bool S57Reader::ApplyRecordUpdate(DDFRecord *poTarget, DDFRecord *poUpdate)
2652
2653
0
{
2654
0
    const char *pszKey = poUpdate->GetField(1)->GetFieldDefn()->GetName();
2655
2656
    /* -------------------------------------------------------------------- */
2657
    /*      Validate versioning.                                            */
2658
    /* -------------------------------------------------------------------- */
2659
0
    if (poTarget->GetIntSubfield(pszKey, 0, "RVER", 0) + 1 !=
2660
0
        poUpdate->GetIntSubfield(pszKey, 0, "RVER", 0))
2661
0
    {
2662
0
        CPLDebug("S57", "Mismatched RVER value on RCNM=%d,RCID=%d.\n",
2663
0
                 poTarget->GetIntSubfield(pszKey, 0, "RCNM", 0),
2664
0
                 poTarget->GetIntSubfield(pszKey, 0, "RCID", 0));
2665
2666
        // CPLAssert( false );
2667
0
        return false;
2668
0
    }
2669
2670
    /* -------------------------------------------------------------------- */
2671
    /*      Update the target version.                                      */
2672
    /* -------------------------------------------------------------------- */
2673
0
    const DDFField *poKey = poTarget->FindField(pszKey);
2674
2675
0
    if (poKey == nullptr)
2676
0
    {
2677
        // CPLAssert( false );
2678
0
        return false;
2679
0
    }
2680
2681
0
    const DDFSubfieldDefn *poRVER_SFD =
2682
0
        poKey->GetFieldDefn()->FindSubfieldDefn("RVER");
2683
0
    if (poRVER_SFD == nullptr)
2684
0
        return false;
2685
0
    if (!EQUAL(poRVER_SFD->GetFormat(), "b12"))
2686
0
    {
2687
0
        CPLError(
2688
0
            CE_Warning, CPLE_NotSupported,
2689
0
            "Subfield RVER of record %s has format=%s, instead of expected b12",
2690
0
            pszKey, poRVER_SFD->GetFormat());
2691
0
        return false;
2692
0
    }
2693
2694
    /* -------------------------------------------------------------------- */
2695
    /*      Update target RVER                                              */
2696
    /* -------------------------------------------------------------------- */
2697
0
    unsigned short nRVER;
2698
0
    int nBytesRemaining = 0;
2699
0
    unsigned char *pachRVER =
2700
0
        reinterpret_cast<unsigned char *>(const_cast<char *>(
2701
0
            poKey->GetSubfieldData(poRVER_SFD, &nBytesRemaining, 0)));
2702
0
    CPLAssert(nBytesRemaining >= static_cast<int>(sizeof(nRVER)));
2703
0
    memcpy(&nRVER, pachRVER, sizeof(nRVER));
2704
0
    CPL_LSBPTR16(&nRVER);
2705
0
    nRVER += 1;
2706
0
    CPL_LSBPTR16(&nRVER);
2707
0
    memcpy(pachRVER, &nRVER, sizeof(nRVER));
2708
2709
    /* -------------------------------------------------------------------- */
2710
    /*      Check for, and apply record record to spatial record pointer    */
2711
    /*      updates.                                                        */
2712
    /* -------------------------------------------------------------------- */
2713
0
    if (poUpdate->FindField("FSPC") != nullptr)
2714
0
    {
2715
0
        const int nFSUI = poUpdate->GetIntSubfield("FSPC", 0, "FSUI", 0);
2716
0
        DDFField *poSrcFSPT = poUpdate->FindField("FSPT");
2717
0
        DDFField *poDstFSPT = poTarget->FindField("FSPT");
2718
2719
0
        if ((poSrcFSPT == nullptr && nFSUI != 2) || poDstFSPT == nullptr)
2720
0
        {
2721
            // CPLAssert( false );
2722
0
            return false;
2723
0
        }
2724
2725
0
        const int nFSIX = poUpdate->GetIntSubfield("FSPC", 0, "FSIX", 0);
2726
0
        const int nNSPT = poUpdate->GetIntSubfield("FSPC", 0, "NSPT", 0);
2727
2728
0
        int nPtrSize = poDstFSPT->GetFieldDefn()->GetFixedWidth();
2729
2730
0
        if (nFSUI == 1) /* INSERT */
2731
0
        {
2732
0
            int nInsertionBytes = nPtrSize * nNSPT;
2733
2734
0
            if (poSrcFSPT->GetDataSize() < nInsertionBytes)
2735
0
            {
2736
0
                CPLDebug("S57",
2737
0
                         "Not enough bytes in source FSPT field. "
2738
0
                         "Has %d, requires %d",
2739
0
                         poSrcFSPT->GetDataSize(), nInsertionBytes);
2740
0
                return false;
2741
0
            }
2742
2743
0
            char *pachInsertion =
2744
0
                static_cast<char *>(CPLMalloc(nInsertionBytes + nPtrSize));
2745
0
            memcpy(pachInsertion, poSrcFSPT->GetData(), nInsertionBytes);
2746
2747
            /*
2748
            ** If we are inserting before an instance that already
2749
            ** exists, we must add it to the end of the data being
2750
            ** inserted.
2751
            */
2752
0
            if (nFSIX <= poDstFSPT->GetRepeatCount())
2753
0
            {
2754
0
                if (poDstFSPT->GetDataSize() < nPtrSize * nFSIX)
2755
0
                {
2756
0
                    CPLDebug("S57",
2757
0
                             "Not enough bytes in dest FSPT field. "
2758
0
                             "Has %d, requires %d",
2759
0
                             poDstFSPT->GetDataSize(), nPtrSize * nFSIX);
2760
0
                    CPLFree(pachInsertion);
2761
0
                    return false;
2762
0
                }
2763
2764
0
                memcpy(pachInsertion + nInsertionBytes,
2765
0
                       poDstFSPT->GetData() + nPtrSize * (nFSIX - 1), nPtrSize);
2766
0
                nInsertionBytes += nPtrSize;
2767
0
            }
2768
2769
0
            poTarget->SetFieldRaw(poDstFSPT, nFSIX - 1, pachInsertion,
2770
0
                                  nInsertionBytes);
2771
0
            CPLFree(pachInsertion);
2772
0
        }
2773
0
        else if (nFSUI == 2) /* DELETE */
2774
0
        {
2775
            /* Wipe each deleted coordinate */
2776
0
            for (int i = nNSPT - 1; i >= 0; i--)
2777
0
            {
2778
0
                poTarget->SetFieldRaw(poDstFSPT, i + nFSIX - 1, nullptr, 0);
2779
0
            }
2780
0
        }
2781
0
        else if (nFSUI == 3) /* MODIFY */
2782
0
        {
2783
            /* copy over each ptr */
2784
0
            if (poSrcFSPT->GetDataSize() < nNSPT * nPtrSize)
2785
0
            {
2786
0
                CPLDebug("S57",
2787
0
                         "Not enough bytes in source FSPT field. Has %d, "
2788
0
                         "requires %d",
2789
0
                         poSrcFSPT->GetDataSize(), nNSPT * nPtrSize);
2790
0
                return false;
2791
0
            }
2792
2793
0
            for (int i = 0; i < nNSPT; i++)
2794
0
            {
2795
0
                const char *pachRawData = poSrcFSPT->GetData() + nPtrSize * i;
2796
0
                poTarget->SetFieldRaw(poDstFSPT, i + nFSIX - 1, pachRawData,
2797
0
                                      nPtrSize);
2798
0
            }
2799
0
        }
2800
0
    }
2801
2802
    /* -------------------------------------------------------------------- */
2803
    /*      Check for, and apply vector record to vector record pointer     */
2804
    /*      updates.                                                        */
2805
    /* -------------------------------------------------------------------- */
2806
0
    if (poUpdate->FindField("VRPC") != nullptr)
2807
0
    {
2808
0
        const int nVPUI = poUpdate->GetIntSubfield("VRPC", 0, "VPUI", 0);
2809
0
        DDFField *poSrcVRPT = poUpdate->FindField("VRPT");
2810
0
        DDFField *poDstVRPT = poTarget->FindField("VRPT");
2811
2812
0
        if ((poSrcVRPT == nullptr && nVPUI != 2) || poDstVRPT == nullptr)
2813
0
        {
2814
            // CPLAssert( false );
2815
0
            return false;
2816
0
        }
2817
2818
0
        const int nVPIX = poUpdate->GetIntSubfield("VRPC", 0, "VPIX", 0);
2819
0
        const int nNVPT = poUpdate->GetIntSubfield("VRPC", 0, "NVPT", 0);
2820
2821
0
        const int nPtrSize = poDstVRPT->GetFieldDefn()->GetFixedWidth();
2822
2823
0
        if (nVPUI == 1) /* INSERT */
2824
0
        {
2825
0
            int nInsertionBytes = nPtrSize * nNVPT;
2826
2827
0
            if (poSrcVRPT->GetDataSize() < nInsertionBytes)
2828
0
            {
2829
0
                CPLDebug("S57",
2830
0
                         "Not enough bytes in source VRPT field. Has %d, "
2831
0
                         "requires %d",
2832
0
                         poSrcVRPT->GetDataSize(), nInsertionBytes);
2833
0
                return false;
2834
0
            }
2835
2836
0
            char *pachInsertion =
2837
0
                static_cast<char *>(CPLMalloc(nInsertionBytes + nPtrSize));
2838
0
            memcpy(pachInsertion, poSrcVRPT->GetData(), nInsertionBytes);
2839
2840
            /*
2841
            ** If we are inserting before an instance that already
2842
            ** exists, we must add it to the end of the data being
2843
            ** inserted.
2844
            */
2845
0
            if (nVPIX <= poDstVRPT->GetRepeatCount())
2846
0
            {
2847
0
                if (poDstVRPT->GetDataSize() < nPtrSize * nVPIX)
2848
0
                {
2849
0
                    CPLDebug("S57",
2850
0
                             "Not enough bytes in dest VRPT field. Has %d, "
2851
0
                             "requires %d",
2852
0
                             poDstVRPT->GetDataSize(), nPtrSize * nVPIX);
2853
0
                    CPLFree(pachInsertion);
2854
0
                    return false;
2855
0
                }
2856
2857
0
                memcpy(pachInsertion + nInsertionBytes,
2858
0
                       poDstVRPT->GetData() + nPtrSize * (nVPIX - 1), nPtrSize);
2859
0
                nInsertionBytes += nPtrSize;
2860
0
            }
2861
2862
0
            poTarget->SetFieldRaw(poDstVRPT, nVPIX - 1, pachInsertion,
2863
0
                                  nInsertionBytes);
2864
0
            CPLFree(pachInsertion);
2865
0
        }
2866
0
        else if (nVPUI == 2) /* DELETE */
2867
0
        {
2868
            /* Wipe each deleted coordinate */
2869
0
            for (int i = nNVPT - 1; i >= 0; i--)
2870
0
            {
2871
0
                poTarget->SetFieldRaw(poDstVRPT, i + nVPIX - 1, nullptr, 0);
2872
0
            }
2873
0
        }
2874
0
        else if (nVPUI == 3) /* MODIFY */
2875
0
        {
2876
0
            if (poSrcVRPT->GetDataSize() < nNVPT * nPtrSize)
2877
0
            {
2878
0
                CPLDebug("S57",
2879
0
                         "Not enough bytes in source VRPT field. "
2880
0
                         "Has %d, requires %d",
2881
0
                         poSrcVRPT->GetDataSize(), nNVPT * nPtrSize);
2882
0
                return false;
2883
0
            }
2884
2885
            /* copy over each ptr */
2886
0
            for (int i = 0; i < nNVPT; i++)
2887
0
            {
2888
0
                const char *pachRawData = poSrcVRPT->GetData() + nPtrSize * i;
2889
2890
0
                poTarget->SetFieldRaw(poDstVRPT, i + nVPIX - 1, pachRawData,
2891
0
                                      nPtrSize);
2892
0
            }
2893
0
        }
2894
0
    }
2895
2896
    /* -------------------------------------------------------------------- */
2897
    /*      Check for, and apply record update to coordinates.              */
2898
    /* -------------------------------------------------------------------- */
2899
0
    if (poUpdate->FindField("SGCC") != nullptr)
2900
0
    {
2901
0
        DDFField *poSrcSG2D = poUpdate->FindField("SG2D");
2902
0
        DDFField *poDstSG2D = poTarget->FindField("SG2D");
2903
2904
0
        const int nCCUI = poUpdate->GetIntSubfield("SGCC", 0, "CCUI", 0);
2905
2906
        /* If we don't have SG2D, check for SG3D */
2907
0
        if (poDstSG2D == nullptr)
2908
0
        {
2909
0
            poDstSG2D = poTarget->FindField("SG3D");
2910
0
            if (poDstSG2D != nullptr)
2911
0
            {
2912
0
                poSrcSG2D = poUpdate->FindField("SG3D");
2913
0
            }
2914
0
            else
2915
0
            {
2916
0
                if (nCCUI != 1)
2917
0
                {
2918
                    // CPLAssert( false );
2919
0
                    return false;
2920
0
                }
2921
2922
0
                poTarget->AddField(
2923
0
                    poTarget->GetModule()->FindFieldDefn("SG2D"));
2924
0
                poDstSG2D = poTarget->FindField("SG2D");
2925
0
                if (poDstSG2D == nullptr)
2926
0
                {
2927
                    // CPLAssert( false );
2928
0
                    return false;
2929
0
                }
2930
2931
                // Delete null default data that was created
2932
0
                poTarget->SetFieldRaw(poDstSG2D, 0, nullptr, 0);
2933
0
            }
2934
0
        }
2935
2936
0
        if (poSrcSG2D == nullptr && nCCUI != 2)
2937
0
        {
2938
            // CPLAssert( false );
2939
0
            return false;
2940
0
        }
2941
2942
0
        int nCoordSize = poDstSG2D->GetFieldDefn()->GetFixedWidth();
2943
0
        const int nCCIX = poUpdate->GetIntSubfield("SGCC", 0, "CCIX", 0);
2944
0
        const int nCCNC = poUpdate->GetIntSubfield("SGCC", 0, "CCNC", 0);
2945
2946
0
        if (nCCUI == 1) /* INSERT */
2947
0
        {
2948
0
            int nInsertionBytes = nCoordSize * nCCNC;
2949
2950
0
            if (poSrcSG2D->GetDataSize() < nInsertionBytes)
2951
0
            {
2952
0
                CPLDebug("S57",
2953
0
                         "Not enough bytes in source SG2D field. "
2954
0
                         "Has %d, requires %d",
2955
0
                         poSrcSG2D->GetDataSize(), nInsertionBytes);
2956
0
                return false;
2957
0
            }
2958
2959
0
            char *pachInsertion =
2960
0
                static_cast<char *>(CPLMalloc(nInsertionBytes + nCoordSize));
2961
0
            memcpy(pachInsertion, poSrcSG2D->GetData(), nInsertionBytes);
2962
2963
            /*
2964
            ** If we are inserting before an instance that already
2965
            ** exists, we must add it to the end of the data being
2966
            ** inserted.
2967
            */
2968
0
            if (nCCIX <= poDstSG2D->GetRepeatCount())
2969
0
            {
2970
0
                if (poDstSG2D->GetDataSize() < nCoordSize * nCCIX)
2971
0
                {
2972
0
                    CPLDebug("S57",
2973
0
                             "Not enough bytes in dest SG2D field. "
2974
0
                             "Has %d, requires %d",
2975
0
                             poDstSG2D->GetDataSize(), nCoordSize * nCCIX);
2976
0
                    CPLFree(pachInsertion);
2977
0
                    return false;
2978
0
                }
2979
2980
0
                memcpy(pachInsertion + nInsertionBytes,
2981
0
                       poDstSG2D->GetData() + nCoordSize * (nCCIX - 1),
2982
0
                       nCoordSize);
2983
0
                nInsertionBytes += nCoordSize;
2984
0
            }
2985
2986
0
            poTarget->SetFieldRaw(poDstSG2D, nCCIX - 1, pachInsertion,
2987
0
                                  nInsertionBytes);
2988
0
            CPLFree(pachInsertion);
2989
0
        }
2990
0
        else if (nCCUI == 2) /* DELETE */
2991
0
        {
2992
            /* Wipe each deleted coordinate */
2993
0
            for (int i = nCCNC - 1; i >= 0; i--)
2994
0
            {
2995
0
                poTarget->SetFieldRaw(poDstSG2D, i + nCCIX - 1, nullptr, 0);
2996
0
            }
2997
0
        }
2998
0
        else if (nCCUI == 3) /* MODIFY */
2999
0
        {
3000
0
            if (poSrcSG2D->GetDataSize() < nCCNC * nCoordSize)
3001
0
            {
3002
0
                CPLDebug("S57",
3003
0
                         "Not enough bytes in source SG2D field. "
3004
0
                         "Has %d, requires %d",
3005
0
                         poSrcSG2D->GetDataSize(), nCCNC * nCoordSize);
3006
0
                return false;
3007
0
            }
3008
3009
            /* copy over each ptr */
3010
0
            for (int i = 0; i < nCCNC; i++)
3011
0
            {
3012
0
                const char *pachRawData = poSrcSG2D->GetData() + nCoordSize * i;
3013
3014
0
                poTarget->SetFieldRaw(poDstSG2D, i + nCCIX - 1, pachRawData,
3015
0
                                      nCoordSize);
3016
0
            }
3017
0
        }
3018
0
    }
3019
3020
    /* -------------------------------------------------------------------- */
3021
    /*      Apply updates to Feature to Feature pointer fields.  Note       */
3022
    /*      INSERT and DELETE are untested.  UPDATE tested per bug #5028.   */
3023
    /* -------------------------------------------------------------------- */
3024
0
    if (poUpdate->FindField("FFPC") != nullptr)
3025
0
    {
3026
0
        int nFFUI = poUpdate->GetIntSubfield("FFPC", 0, "FFUI", 0);
3027
0
        DDFField *poSrcFFPT = poUpdate->FindField("FFPT");
3028
0
        DDFField *poDstFFPT = poTarget->FindField("FFPT");
3029
3030
0
        if ((poSrcFFPT == nullptr && nFFUI != 2) ||
3031
0
            (poDstFFPT == nullptr && nFFUI != 1))
3032
0
        {
3033
0
            CPLDebug("S57", "Missing source or target FFPT applying update.");
3034
            // CPLAssert( false );
3035
0
            return false;
3036
0
        }
3037
3038
        // Create FFPT field on target record, if it does not yet exist.
3039
0
        if (poDstFFPT == nullptr)
3040
0
        {
3041
            // Untested!
3042
0
            poTarget->AddField(poTarget->GetModule()->FindFieldDefn("FFPT"));
3043
0
            poDstFFPT = poTarget->FindField("FFPT");
3044
0
            if (poDstFFPT == nullptr)
3045
0
            {
3046
                // CPLAssert( false );
3047
0
                return false;
3048
0
            }
3049
3050
            // Delete null default data that was created
3051
0
            poTarget->SetFieldRaw(poDstFFPT, 0, nullptr, 0);
3052
0
        }
3053
3054
        // FFPT includes COMT which is variable length which would
3055
        // greatly complicate updates.  But in practice COMT is always
3056
        // an empty string so we will take a chance and assume that so
3057
        // we have a fixed record length.  We *could* actually verify that
3058
        // but I have not done so for now.
3059
0
        const int nFFPTSize = 10;
3060
0
        const int nFFIX = poUpdate->GetIntSubfield("FFPC", 0, "FFIX", 0);
3061
0
        const int nNFPT = poUpdate->GetIntSubfield("FFPC", 0, "NFPT", 0);
3062
3063
0
        if (nFFUI == 1) /* INSERT */
3064
0
        {
3065
            // Untested!
3066
0
            CPLDebug("S57", "Using untested FFPT INSERT code!");
3067
3068
0
            int nInsertionBytes = nFFPTSize * nNFPT;
3069
3070
0
            if (poSrcFFPT->GetDataSize() < nInsertionBytes)
3071
0
            {
3072
0
                CPLDebug("S57",
3073
0
                         "Not enough bytes in source FFPT field. "
3074
0
                         "Has %d, requires %d",
3075
0
                         poSrcFFPT->GetDataSize(), nInsertionBytes);
3076
0
                return false;
3077
0
            }
3078
3079
0
            char *pachInsertion =
3080
0
                static_cast<char *>(CPLMalloc(nInsertionBytes + nFFPTSize));
3081
0
            memcpy(pachInsertion, poSrcFFPT->GetData(), nInsertionBytes);
3082
3083
            /*
3084
            ** If we are inserting before an instance that already
3085
            ** exists, we must add it to the end of the data being
3086
            ** inserted.
3087
            */
3088
0
            if (nFFIX <= poDstFFPT->GetRepeatCount())
3089
0
            {
3090
0
                if (poDstFFPT->GetDataSize() < nFFPTSize * nFFIX)
3091
0
                {
3092
0
                    CPLDebug("S57",
3093
0
                             "Not enough bytes in dest FFPT field. "
3094
0
                             "Has %d, requires %d",
3095
0
                             poDstFFPT->GetDataSize(), nFFPTSize * nFFIX);
3096
0
                    CPLFree(pachInsertion);
3097
0
                    return false;
3098
0
                }
3099
3100
0
                memcpy(pachInsertion + nInsertionBytes,
3101
0
                       poDstFFPT->GetData() + nFFPTSize * (nFFIX - 1),
3102
0
                       nFFPTSize);
3103
0
                nInsertionBytes += nFFPTSize;
3104
0
            }
3105
3106
0
            poTarget->SetFieldRaw(poDstFFPT, nFFIX - 1, pachInsertion,
3107
0
                                  nInsertionBytes);
3108
0
            CPLFree(pachInsertion);
3109
0
        }
3110
0
        else if (nFFUI == 2) /* DELETE */
3111
0
        {
3112
            // Untested!
3113
0
            CPLDebug("S57", "Using untested FFPT DELETE code!");
3114
3115
            /* Wipe each deleted record */
3116
0
            for (int i = nNFPT - 1; i >= 0; i--)
3117
0
            {
3118
0
                poTarget->SetFieldRaw(poDstFFPT, i + nFFIX - 1, nullptr, 0);
3119
0
            }
3120
0
        }
3121
0
        else if (nFFUI == 3) /* UPDATE */
3122
0
        {
3123
0
            if (poSrcFFPT->GetDataSize() < nNFPT * nFFPTSize)
3124
0
            {
3125
0
                CPLDebug("S57",
3126
0
                         "Not enough bytes in source FFPT field. "
3127
0
                         "Has %d, requires %d",
3128
0
                         poSrcFFPT->GetDataSize(), nNFPT * nFFPTSize);
3129
0
                return false;
3130
0
            }
3131
3132
            /* copy over each ptr */
3133
0
            for (int i = 0; i < nNFPT; i++)
3134
0
            {
3135
0
                const char *pachRawData = poSrcFFPT->GetData() + nFFPTSize * i;
3136
3137
0
                poTarget->SetFieldRaw(poDstFFPT, i + nFFIX - 1, pachRawData,
3138
0
                                      nFFPTSize);
3139
0
            }
3140
0
        }
3141
0
    }
3142
3143
    /* -------------------------------------------------------------------- */
3144
    /*      Check for and apply changes to attribute lists.                 */
3145
    /* -------------------------------------------------------------------- */
3146
0
    if (poUpdate->FindField("ATTF") != nullptr)
3147
0
    {
3148
0
        DDFField *poDstATTF = poTarget->FindField("ATTF");
3149
3150
0
        if (poDstATTF == nullptr)
3151
0
        {
3152
            // Create empty ATTF Field (see GDAL/OGR Bug #1648)" );
3153
0
            poDstATTF = poTarget->AddField(poModule->FindFieldDefn("ATTF"));
3154
0
        }
3155
3156
0
        DDFField *poSrcATTF = poUpdate->FindField("ATTF");
3157
0
        const int nRepeatCount = poSrcATTF->GetRepeatCount();
3158
3159
0
        for (int iAtt = 0; iAtt < nRepeatCount; iAtt++)
3160
0
        {
3161
0
            const int nATTL = poUpdate->GetIntSubfield("ATTF", 0, "ATTL", iAtt);
3162
0
            int iTAtt = poDstATTF->GetRepeatCount() - 1;  // Used after for.
3163
3164
0
            for (; iTAtt >= 0; iTAtt--)
3165
0
            {
3166
0
                if (poTarget->GetIntSubfield("ATTF", 0, "ATTL", iTAtt) == nATTL)
3167
0
                    break;
3168
0
            }
3169
0
            if (iTAtt == -1)
3170
0
                iTAtt = poDstATTF->GetRepeatCount();
3171
3172
0
            int nDataBytes = 0;
3173
0
            const char *pszRawData =
3174
0
                poSrcATTF->GetInstanceData(iAtt, &nDataBytes);
3175
0
            if (pszRawData[2] == 0x7f /* delete marker */)
3176
0
            {
3177
0
                poTarget->SetFieldRaw(poDstATTF, iTAtt, nullptr, 0);
3178
0
            }
3179
0
            else
3180
0
            {
3181
0
                poTarget->SetFieldRaw(poDstATTF, iTAtt, pszRawData, nDataBytes);
3182
0
            }
3183
0
        }
3184
0
    }
3185
3186
0
    return true;
3187
0
}
3188
3189
/************************************************************************/
3190
/*                            ApplyUpdates()                            */
3191
/*                                                                      */
3192
/*      Read records from an update file, and apply them to the         */
3193
/*      currently loaded index of features.                             */
3194
/************************************************************************/
3195
3196
bool S57Reader::ApplyUpdates(DDFModule *poUpdateModule)
3197
3198
0
{
3199
    /* -------------------------------------------------------------------- */
3200
    /*      Ensure base file is loaded.                                     */
3201
    /* -------------------------------------------------------------------- */
3202
0
    if (!bFileIngested && !Ingest())
3203
0
        return false;
3204
3205
    /* -------------------------------------------------------------------- */
3206
    /*      Read records, and apply as updates.                             */
3207
    /* -------------------------------------------------------------------- */
3208
0
    CPLErrorReset();
3209
3210
0
    DDFRecord *poRecord = nullptr;
3211
3212
0
    while ((poRecord = poUpdateModule->ReadRecord()) != nullptr)
3213
0
    {
3214
0
        const DDFField *poKeyField = poRecord->GetField(1);
3215
0
        if (poKeyField == nullptr)
3216
0
            return false;
3217
3218
0
        const char *pszKey = poKeyField->GetFieldDefn()->GetName();
3219
3220
0
        if (EQUAL(pszKey, "VRID") || EQUAL(pszKey, "FRID"))
3221
0
        {
3222
0
            const int nRCNM = poRecord->GetIntSubfield(pszKey, 0, "RCNM", 0);
3223
0
            const int nRCID = poRecord->GetIntSubfield(pszKey, 0, "RCID", 0);
3224
0
            const int nRVER = poRecord->GetIntSubfield(pszKey, 0, "RVER", 0);
3225
0
            const int nRUIN = poRecord->GetIntSubfield(pszKey, 0, "RUIN", 0);
3226
0
            DDFRecordIndex *poIndex = nullptr;
3227
3228
0
            if (EQUAL(poKeyField->GetFieldDefn()->GetName(), "VRID"))
3229
0
            {
3230
0
                switch (nRCNM)
3231
0
                {
3232
0
                    case RCNM_VI:
3233
0
                        poIndex = &oVI_Index;
3234
0
                        break;
3235
3236
0
                    case RCNM_VC:
3237
0
                        poIndex = &oVC_Index;
3238
0
                        break;
3239
3240
0
                    case RCNM_VE:
3241
0
                        poIndex = &oVE_Index;
3242
0
                        break;
3243
3244
0
                    case RCNM_VF:
3245
0
                        poIndex = &oVF_Index;
3246
0
                        break;
3247
3248
0
                    default:
3249
                        // CPLAssert( false );
3250
0
                        return false;
3251
0
                        break;
3252
0
                }
3253
0
            }
3254
0
            else
3255
0
            {
3256
0
                poIndex = &oFE_Index;
3257
0
            }
3258
3259
0
            if (poIndex != nullptr)
3260
0
            {
3261
0
                if (nRUIN == 1) /* insert */
3262
0
                {
3263
0
                    poIndex->AddRecord(nRCID, poRecord->CloneOn(poModule));
3264
0
                }
3265
0
                else if (nRUIN == 2) /* delete */
3266
0
                {
3267
0
                    DDFRecord *poTarget = poIndex->FindRecord(nRCID);
3268
0
                    if (poTarget == nullptr)
3269
0
                    {
3270
0
                        CPLError(CE_Warning, CPLE_AppDefined,
3271
0
                                 "Can't find RCNM=%d,RCID=%d for delete.\n",
3272
0
                                 nRCNM, nRCID);
3273
0
                    }
3274
0
                    else if (poTarget->GetIntSubfield(pszKey, 0, "RVER", 0) !=
3275
0
                             nRVER - 1)
3276
0
                    {
3277
0
                        CPLError(CE_Warning, CPLE_AppDefined,
3278
0
                                 "Mismatched RVER value on RCNM=%d,RCID=%d.\n",
3279
0
                                 nRCNM, nRCID);
3280
0
                    }
3281
0
                    else
3282
0
                    {
3283
0
                        poIndex->RemoveRecord(nRCID);
3284
0
                    }
3285
0
                }
3286
3287
0
                else if (nRUIN == 3) /* modify in place */
3288
0
                {
3289
0
                    DDFRecord *poTarget = poIndex->FindRecord(nRCID);
3290
0
                    if (poTarget == nullptr)
3291
0
                    {
3292
0
                        CPLError(CE_Warning, CPLE_AppDefined,
3293
0
                                 "Can't find RCNM=%d,RCID=%d for update.\n",
3294
0
                                 nRCNM, nRCID);
3295
0
                    }
3296
0
                    else
3297
0
                    {
3298
0
                        if (!ApplyRecordUpdate(poTarget, poRecord))
3299
0
                        {
3300
0
                            CPLError(CE_Warning, CPLE_AppDefined,
3301
0
                                     "An update to RCNM=%d,RCID=%d failed.\n",
3302
0
                                     nRCNM, nRCID);
3303
0
                        }
3304
0
                    }
3305
0
                }
3306
0
            }
3307
0
        }
3308
3309
0
        else if (EQUAL(pszKey, "DSID"))
3310
0
        {
3311
0
            const char *pszEDTN =
3312
0
                poRecord->GetStringSubfield("DSID", 0, "EDTN", 0);
3313
0
            if (pszEDTN != nullptr)
3314
0
            {
3315
0
                if (!m_osEDTNUpdate.empty())
3316
0
                {
3317
0
                    if (!EQUAL(pszEDTN, "0") &&  // cancel
3318
0
                        !EQUAL(pszEDTN, m_osEDTNUpdate.c_str()))
3319
0
                    {
3320
0
                        CPLDebug("S57",
3321
0
                                 "Skipping update as EDTN=%s in update does "
3322
0
                                 "not match expected %s.",
3323
0
                                 pszEDTN, m_osEDTNUpdate.c_str());
3324
0
                        return false;
3325
0
                    }
3326
0
                }
3327
0
                m_osEDTNUpdate = pszEDTN;
3328
0
            }
3329
3330
0
            const char *pszUPDN =
3331
0
                poRecord->GetStringSubfield("DSID", 0, "UPDN", 0);
3332
0
            if (pszUPDN != nullptr)
3333
0
            {
3334
0
                if (!m_osUPDNUpdate.empty())
3335
0
                {
3336
0
                    if (atoi(m_osUPDNUpdate.c_str()) + 1 != atoi(pszUPDN))
3337
0
                    {
3338
0
                        CPLDebug("S57",
3339
0
                                 "Skipping update as UPDN=%s in update does "
3340
0
                                 "not match expected %d.",
3341
0
                                 pszUPDN, atoi(m_osUPDNUpdate.c_str()) + 1);
3342
0
                        return false;
3343
0
                    }
3344
0
                }
3345
0
                m_osUPDNUpdate = pszUPDN;
3346
0
            }
3347
3348
0
            const char *pszISDT =
3349
0
                poRecord->GetStringSubfield("DSID", 0, "ISDT", 0);
3350
0
            if (pszISDT != nullptr)
3351
0
                m_osISDTUpdate = pszISDT;
3352
0
        }
3353
3354
0
        else
3355
0
        {
3356
0
            CPLDebug("S57",
3357
0
                     "Skipping %s record in S57Reader::ApplyUpdates().\n",
3358
0
                     pszKey);
3359
0
        }
3360
0
    }
3361
3362
0
    return CPLGetLastErrorType() != CE_Failure;
3363
0
}
3364
3365
/************************************************************************/
3366
/*                        FindAndApplyUpdates()                         */
3367
/*                                                                      */
3368
/*      Find all update files that would appear to apply to this        */
3369
/*      base file.                                                      */
3370
/************************************************************************/
3371
3372
bool S57Reader::FindAndApplyUpdates(const char *pszPath)
3373
3374
316
{
3375
316
    if (pszPath == nullptr)
3376
316
        pszPath = pszModuleName;
3377
3378
316
    if (!EQUAL(CPLGetExtensionSafe(pszPath).c_str(), "000"))
3379
316
    {
3380
316
        CPLError(CE_Failure, CPLE_AppDefined,
3381
316
                 "Can't apply updates to a base file with a different\n"
3382
316
                 "extension than .000.\n");
3383
316
        return false;
3384
316
    }
3385
3386
0
    bool bSuccess = true;
3387
3388
0
    for (int iUpdate = 1; bSuccess; iUpdate++)
3389
0
    {
3390
        // Creaing file extension
3391
0
        CPLString extension;
3392
0
        CPLString dirname;
3393
3394
0
        if (iUpdate < 10)
3395
0
        {
3396
0
            char buf[2];
3397
0
            CPLsnprintf(buf, sizeof(buf), "%i", iUpdate);
3398
0
            extension.append("00");
3399
0
            extension.append(buf);
3400
0
            dirname.append(buf);
3401
0
        }
3402
0
        else if (iUpdate < 100)
3403
0
        {
3404
0
            char buf[3];
3405
0
            CPLsnprintf(buf, sizeof(buf), "%i", iUpdate);
3406
0
            extension.append("0");
3407
0
            extension.append(buf);
3408
0
            dirname.append(buf);
3409
0
        }
3410
0
        else if (iUpdate < 1000)
3411
0
        {
3412
0
            char buf[4];
3413
0
            CPLsnprintf(buf, sizeof(buf), "%i", iUpdate);
3414
0
            extension.append(buf);
3415
0
            dirname.append(buf);
3416
0
        }
3417
3418
0
        DDFModule oUpdateModule;
3419
3420
        // trying current dir first
3421
0
        char *pszUpdateFilename = CPLStrdup(
3422
0
            CPLResetExtensionSafe(pszPath, extension.c_str()).c_str());
3423
3424
0
        VSILFILE *file = VSIFOpenL(pszUpdateFilename, "r");
3425
0
        if (file)
3426
0
        {
3427
0
            VSIFCloseL(file);
3428
0
            bSuccess = CPL_TO_BOOL(oUpdateModule.Open(pszUpdateFilename, TRUE));
3429
0
            if (bSuccess)
3430
0
            {
3431
0
                CPLDebug("S57", "Applying feature updates from %s.",
3432
0
                         pszUpdateFilename);
3433
0
                if (!ApplyUpdates(&oUpdateModule))
3434
0
                    return false;
3435
0
            }
3436
0
        }
3437
0
        else  // File is store on Primar generated CD.
3438
0
        {
3439
0
            char *pszBaseFileDir =
3440
0
                CPLStrdup(CPLGetDirnameSafe(pszPath).c_str());
3441
0
            char *pszFileDir =
3442
0
                CPLStrdup(CPLGetDirnameSafe(pszBaseFileDir).c_str());
3443
3444
0
            CPLString remotefile(pszFileDir);
3445
0
            remotefile.append("/");
3446
0
            remotefile.append(dirname);
3447
0
            remotefile.append("/");
3448
0
            remotefile.append(CPLGetBasenameSafe(pszPath).c_str());
3449
0
            remotefile.append(".");
3450
0
            remotefile.append(extension);
3451
0
            bSuccess =
3452
0
                CPL_TO_BOOL(oUpdateModule.Open(remotefile.c_str(), TRUE));
3453
3454
0
            if (bSuccess)
3455
0
                CPLDebug("S57", "Applying feature updates from %s.",
3456
0
                         remotefile.c_str());
3457
0
            CPLFree(pszBaseFileDir);
3458
0
            CPLFree(pszFileDir);
3459
0
            if (bSuccess)
3460
0
            {
3461
0
                if (!ApplyUpdates(&oUpdateModule))
3462
0
                    return false;
3463
0
            }
3464
0
        }  // end for if-else
3465
0
        CPLFree(pszUpdateFilename);
3466
0
    }
3467
3468
0
    return true;
3469
0
}
3470
3471
/************************************************************************/
3472
/*                             GetExtent()                              */
3473
/*                                                                      */
3474
/*      Scan all the cached records collecting spatial bounds as        */
3475
/*      efficiently as possible for this transfer.                      */
3476
/************************************************************************/
3477
3478
OGRErr S57Reader::GetExtent(OGREnvelope *psExtent, int bForce)
3479
3480
0
{
3481
    /* -------------------------------------------------------------------- */
3482
    /*      If we aren't forced to get the extent say no if we haven't      */
3483
    /*      already indexed the iso8211 records.                            */
3484
    /* -------------------------------------------------------------------- */
3485
0
    if (!bForce && !bFileIngested)
3486
0
        return OGRERR_FAILURE;
3487
3488
0
    if (!Ingest())
3489
0
        return OGRERR_FAILURE;
3490
3491
    /* -------------------------------------------------------------------- */
3492
    /*      We will scan all the low level vector elements for extents      */
3493
    /*      coordinates.                                                    */
3494
    /* -------------------------------------------------------------------- */
3495
0
    bool bGotExtents = false;
3496
0
    int nXMin = 0;
3497
0
    int nXMax = 0;
3498
0
    int nYMin = 0;
3499
0
    int nYMax = 0;
3500
3501
0
    const int INDEX_COUNT = 4;
3502
0
    DDFRecordIndex *apoIndex[INDEX_COUNT];
3503
3504
0
    apoIndex[0] = &oVI_Index;
3505
0
    apoIndex[1] = &oVC_Index;
3506
0
    apoIndex[2] = &oVE_Index;
3507
0
    apoIndex[3] = &oVF_Index;
3508
3509
0
    for (int iIndex = 0; iIndex < INDEX_COUNT; iIndex++)
3510
0
    {
3511
0
        DDFRecordIndex *poIndex = apoIndex[iIndex];
3512
3513
0
        for (int iVIndex = 0; iVIndex < poIndex->GetCount(); iVIndex++)
3514
0
        {
3515
0
            DDFRecord *poRecord = poIndex->GetByIndex(iVIndex);
3516
0
            DDFField *poSG3D = poRecord->FindField("SG3D");
3517
0
            DDFField *poSG2D = poRecord->FindField("SG2D");
3518
3519
0
            if (poSG3D != nullptr)
3520
0
            {
3521
0
                const int nVCount = poSG3D->GetRepeatCount();
3522
0
                const GByte *pabyData = (const GByte *)poSG3D->GetData();
3523
0
                if (poSG3D->GetDataSize() <
3524
0
                    3 * nVCount * static_cast<int>(sizeof(int)))
3525
0
                    return OGRERR_FAILURE;
3526
3527
0
                for (int i = 0; i < nVCount; i++)
3528
0
                {
3529
0
                    GInt32 nX = CPL_LSBSINT32PTR(pabyData + 4 * (i * 3 + 1));
3530
0
                    GInt32 nY = CPL_LSBSINT32PTR(pabyData + 4 * (i * 3 + 0));
3531
3532
0
                    if (bGotExtents)
3533
0
                    {
3534
0
                        nXMin = std::min(nXMin, nX);
3535
0
                        nXMax = std::max(nXMax, nX);
3536
0
                        nYMin = std::min(nYMin, nY);
3537
0
                        nYMax = std::max(nYMax, nY);
3538
0
                    }
3539
0
                    else
3540
0
                    {
3541
0
                        nXMin = nX;
3542
0
                        nXMax = nX;
3543
0
                        nYMin = nY;
3544
0
                        nYMax = nY;
3545
0
                        bGotExtents = true;
3546
0
                    }
3547
0
                }
3548
0
            }
3549
0
            else if (poSG2D != nullptr)
3550
0
            {
3551
0
                const int nVCount = poSG2D->GetRepeatCount();
3552
3553
0
                if (poSG2D->GetDataSize() < 2 * nVCount * (int)sizeof(int))
3554
0
                    return OGRERR_FAILURE;
3555
3556
0
                const GByte *pabyData = (const GByte *)poSG2D->GetData();
3557
3558
0
                for (int i = 0; i < nVCount; i++)
3559
0
                {
3560
0
                    const GInt32 nX =
3561
0
                        CPL_LSBSINT32PTR(pabyData + 4 * (i * 2 + 1));
3562
0
                    const GInt32 nY =
3563
0
                        CPL_LSBSINT32PTR(pabyData + 4 * (i * 2 + 0));
3564
3565
0
                    if (bGotExtents)
3566
0
                    {
3567
0
                        nXMin = std::min(nXMin, nX);
3568
0
                        nXMax = std::max(nXMax, nX);
3569
0
                        nYMin = std::min(nYMin, nY);
3570
0
                        nYMax = std::max(nYMax, nY);
3571
0
                    }
3572
0
                    else
3573
0
                    {
3574
0
                        nXMin = nX;
3575
0
                        nXMax = nX;
3576
0
                        nYMin = nY;
3577
0
                        nYMax = nY;
3578
0
                        bGotExtents = true;
3579
0
                    }
3580
0
                }
3581
0
            }
3582
0
        }
3583
0
    }
3584
3585
0
    if (!bGotExtents)
3586
0
    {
3587
0
        return OGRERR_FAILURE;
3588
0
    }
3589
0
    else
3590
0
    {
3591
0
        psExtent->MinX = nXMin / static_cast<double>(nCOMF);
3592
0
        psExtent->MaxX = nXMax / static_cast<double>(nCOMF);
3593
0
        psExtent->MinY = nYMin / static_cast<double>(nCOMF);
3594
0
        psExtent->MaxY = nYMax / static_cast<double>(nCOMF);
3595
3596
0
        return OGRERR_NONE;
3597
0
    }
3598
0
}