Coverage Report

Created: 2026-08-14 09:29

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