Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/s57/s57writer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  S-57 Translator
4
 * Purpose:  Implements S57Writer class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2003, Frank Warmerdam
9
 * Copyright (c) 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
/************************************************************************/
20
/*                             S57Writer()                              */
21
/************************************************************************/
22
23
S57Writer::S57Writer()
24
0
    : poModule(nullptr), nNext0001Index(0), poRegistrar(nullptr),
25
0
      poClassContentExplorer(nullptr), m_nCOMF(nDEFAULT_COMF),
26
0
      m_nSOMF(nDEFAULT_SOMF)
27
0
{
28
0
}
29
30
/************************************************************************/
31
/*                             ~S57Writer()                             */
32
/************************************************************************/
33
34
S57Writer::~S57Writer()
35
36
0
{
37
0
    Close();
38
0
}
39
40
/************************************************************************/
41
/*                               Close()                                */
42
/*                                                                      */
43
/*      Close the current S-57 dataset.                                 */
44
/************************************************************************/
45
46
bool S57Writer::Close()
47
48
0
{
49
0
    if (poModule != nullptr)
50
0
    {
51
0
        poModule->Close();
52
0
        delete poModule;
53
0
        poModule = nullptr;
54
0
    }
55
0
    return true;
56
0
}
57
58
/************************************************************************/
59
/*                           CreateS57File()                            */
60
/*                                                                      */
61
/*      Create a new output ISO8211 file with all the S-57 data         */
62
/*      definitions.                                                    */
63
/************************************************************************/
64
65
bool S57Writer::CreateS57File(const char *pszFilename)
66
67
0
{
68
    // TODO: What was oModule for if it was unused?
69
    // DDFModule  oModule;
70
0
    Close();
71
72
0
    nNext0001Index = 1;
73
74
    /* -------------------------------------------------------------------- */
75
    /*      Create and initialize new module.                               */
76
    /* -------------------------------------------------------------------- */
77
0
    poModule = new DDFModule();
78
0
    poModule->Initialize();
79
80
    /* -------------------------------------------------------------------- */
81
    /*      Create the '0000' definition.                                   */
82
    /* -------------------------------------------------------------------- */
83
0
    DDFFieldDefn *poFDefn = new DDFFieldDefn();
84
85
0
    poFDefn->Create("0000", "",
86
0
                    "0001DSIDDSIDDSSI0001DSPM0001VRIDVRIDATTVVRIDVRPCVRID"
87
0
                    "VRPTVRIDSGCCVRIDSG2DVRIDSG3D0001FRIDFRIDFOIDFRIDATTF"
88
0
                    "FRIDNATFFRIDFFPCFRIDFFPTFRIDFSPCFRIDFSPT",
89
0
                    dsc_elementary, dtc_char_string);
90
91
0
    poModule->AddField(poFDefn);
92
93
    /* -------------------------------------------------------------------- */
94
    /*      Create the '0001' definition.                                   */
95
    /* -------------------------------------------------------------------- */
96
0
    poFDefn = new DDFFieldDefn();
97
98
0
    poFDefn->Create("0001", "ISO 8211 Record Identifier", "", dsc_elementary,
99
0
                    dtc_bit_string, "(b12)");
100
101
0
    poModule->AddField(poFDefn);
102
103
    /* -------------------------------------------------------------------- */
104
    /*      Create the DSID field.                                          */
105
    /* -------------------------------------------------------------------- */
106
0
    poFDefn = new DDFFieldDefn();
107
108
0
    poFDefn->Create("DSID", "Data set identification field", "", dsc_vector,
109
0
                    dtc_mixed_data_type);
110
111
0
    poFDefn->AddSubfield("RCNM", "b11");
112
0
    poFDefn->AddSubfield("RCID", "b14");
113
0
    poFDefn->AddSubfield("EXPP", "b11");
114
0
    poFDefn->AddSubfield("INTU", "b11");
115
0
    poFDefn->AddSubfield("DSNM", "A");
116
0
    poFDefn->AddSubfield("EDTN", "A");
117
0
    poFDefn->AddSubfield("UPDN", "A");
118
0
    poFDefn->AddSubfield("UADT", "A(8)");
119
0
    poFDefn->AddSubfield("ISDT", "A(8)");
120
0
    poFDefn->AddSubfield("STED", "R(4)");
121
0
    poFDefn->AddSubfield("PRSP", "b11");
122
0
    poFDefn->AddSubfield("PSDN", "A");
123
0
    poFDefn->AddSubfield("PRED", "A");
124
0
    poFDefn->AddSubfield("PROF", "b11");
125
0
    poFDefn->AddSubfield("AGEN", "b12");
126
0
    poFDefn->AddSubfield("COMT", "A");
127
128
0
    poModule->AddField(poFDefn);
129
130
    /* -------------------------------------------------------------------- */
131
    /*      Create the DSSI field.                                          */
132
    /* -------------------------------------------------------------------- */
133
0
    poFDefn = new DDFFieldDefn();
134
135
0
    poFDefn->Create("DSSI", "Data set structure information field", "",
136
0
                    dsc_vector, dtc_mixed_data_type);
137
138
0
    poFDefn->AddSubfield("DSTR", "b11");
139
0
    poFDefn->AddSubfield("AALL", "b11");
140
0
    poFDefn->AddSubfield("NALL", "b11");
141
0
    poFDefn->AddSubfield("NOMR", "b14");
142
0
    poFDefn->AddSubfield("NOCR", "b14");
143
0
    poFDefn->AddSubfield("NOGR", "b14");
144
0
    poFDefn->AddSubfield("NOLR", "b14");
145
0
    poFDefn->AddSubfield("NOIN", "b14");
146
0
    poFDefn->AddSubfield("NOCN", "b14");
147
0
    poFDefn->AddSubfield("NOED", "b14");
148
0
    poFDefn->AddSubfield("NOFA", "b14");
149
150
0
    poModule->AddField(poFDefn);
151
152
    /* -------------------------------------------------------------------- */
153
    /*      Create the DSPM field.                                          */
154
    /* -------------------------------------------------------------------- */
155
0
    poFDefn = new DDFFieldDefn();
156
157
0
    poFDefn->Create("DSPM", "Data set parameter field", "", dsc_vector,
158
0
                    dtc_mixed_data_type);
159
160
0
    poFDefn->AddSubfield("RCNM", "b11");
161
0
    poFDefn->AddSubfield("RCID", "b14");
162
0
    poFDefn->AddSubfield("HDAT", "b11");
163
0
    poFDefn->AddSubfield("VDAT", "b11");
164
0
    poFDefn->AddSubfield("SDAT", "b11");
165
0
    poFDefn->AddSubfield("CSCL", "b14");
166
0
    poFDefn->AddSubfield("DUNI", "b11");
167
0
    poFDefn->AddSubfield("HUNI", "b11");
168
0
    poFDefn->AddSubfield("PUNI", "b11");
169
0
    poFDefn->AddSubfield("COUN", "b11");
170
0
    poFDefn->AddSubfield("COMF", "b14");
171
0
    poFDefn->AddSubfield("SOMF", "b14");
172
0
    poFDefn->AddSubfield("COMT", "A");
173
174
0
    poModule->AddField(poFDefn);
175
176
    /* -------------------------------------------------------------------- */
177
    /*      Create the VRID field.                                          */
178
    /* -------------------------------------------------------------------- */
179
0
    poFDefn = new DDFFieldDefn();
180
181
0
    poFDefn->Create("VRID", "Vector record identifier field", "", dsc_vector,
182
0
                    dtc_mixed_data_type);
183
184
0
    poFDefn->AddSubfield("RCNM", "b11");
185
0
    poFDefn->AddSubfield("RCID", "b14");
186
0
    poFDefn->AddSubfield("RVER", "b12");
187
0
    poFDefn->AddSubfield("RUIN", "b11");
188
189
0
    poModule->AddField(poFDefn);
190
191
    /* -------------------------------------------------------------------- */
192
    /*      Create the VRPC field.                                          */
193
    /* -------------------------------------------------------------------- */
194
0
    poFDefn = new DDFFieldDefn();
195
196
0
    poFDefn->Create("VRPC", "Vector Record Pointer Control field", "",
197
0
                    dsc_vector, dtc_mixed_data_type);
198
199
0
    poFDefn->AddSubfield("VPUI", "b11");
200
0
    poFDefn->AddSubfield("VPIX", "b12");
201
0
    poFDefn->AddSubfield("NVPT", "b12");
202
203
0
    poModule->AddField(poFDefn);
204
205
    /* -------------------------------------------------------------------- */
206
    /*      Create the VRPT field.                                          */
207
    /* -------------------------------------------------------------------- */
208
0
    poFDefn = new DDFFieldDefn();
209
210
0
    poFDefn->Create("VRPT", "Vector record pointer field", "*", dsc_array,
211
0
                    dtc_mixed_data_type);
212
213
0
    poFDefn->AddSubfield("NAME", "B(40)");
214
0
    poFDefn->AddSubfield("ORNT", "b11");
215
0
    poFDefn->AddSubfield("USAG", "b11");
216
0
    poFDefn->AddSubfield("TOPI", "b11");
217
0
    poFDefn->AddSubfield("MASK", "b11");
218
219
0
    poModule->AddField(poFDefn);
220
221
    /* -------------------------------------------------------------------- */
222
    /*      Create the ATTV field.                                          */
223
    /* -------------------------------------------------------------------- */
224
0
    poFDefn = new DDFFieldDefn();
225
226
0
    poFDefn->Create("ATTV", "Vector record attribute field", "*", dsc_array,
227
0
                    dtc_mixed_data_type);
228
229
0
    poFDefn->AddSubfield("ATTL", "b12");
230
0
    poFDefn->AddSubfield("ATVL", "A");
231
232
0
    poModule->AddField(poFDefn);
233
234
    /* -------------------------------------------------------------------- */
235
    /*      Create the SGCC field.                                          */
236
    /* -------------------------------------------------------------------- */
237
0
    poFDefn = new DDFFieldDefn();
238
239
0
    poFDefn->Create("SGCC", "Coordinate Control Field", "", dsc_vector,
240
0
                    dtc_mixed_data_type);
241
242
0
    poFDefn->AddSubfield("CCUI", "b11");
243
0
    poFDefn->AddSubfield("CCIX", "b12");
244
0
    poFDefn->AddSubfield("CCNC", "b12");
245
246
0
    poModule->AddField(poFDefn);
247
248
    /* -------------------------------------------------------------------- */
249
    /*      Create the SG2D field.                                          */
250
    /* -------------------------------------------------------------------- */
251
0
    poFDefn = new DDFFieldDefn();
252
253
0
    poFDefn->Create("SG2D", "2-D coordinate field", "*", dsc_array,
254
0
                    dtc_bit_string);
255
256
0
    poFDefn->AddSubfield("YCOO", "b24");
257
0
    poFDefn->AddSubfield("XCOO", "b24");
258
259
0
    poModule->AddField(poFDefn);
260
261
    /* -------------------------------------------------------------------- */
262
    /*      Create the SG3D field.                                          */
263
    /* -------------------------------------------------------------------- */
264
0
    poFDefn = new DDFFieldDefn();
265
266
0
    poFDefn->Create("SG3D", "3-D coordinate (sounding array) field", "*",
267
0
                    dsc_array, dtc_bit_string);
268
269
0
    poFDefn->AddSubfield("YCOO", "b24");
270
0
    poFDefn->AddSubfield("XCOO", "b24");
271
0
    poFDefn->AddSubfield("VE3D", "b24");
272
273
0
    poModule->AddField(poFDefn);
274
275
    /* -------------------------------------------------------------------- */
276
    /*      Create the FRID field.                                          */
277
    /* -------------------------------------------------------------------- */
278
0
    poFDefn = new DDFFieldDefn();
279
280
0
    poFDefn->Create("FRID", "Feature record identifier field", "", dsc_vector,
281
0
                    dtc_mixed_data_type);
282
283
0
    poFDefn->AddSubfield("RCNM", "b11");
284
0
    poFDefn->AddSubfield("RCID", "b14");
285
0
    poFDefn->AddSubfield("PRIM", "b11");
286
0
    poFDefn->AddSubfield("GRUP", "b11");
287
0
    poFDefn->AddSubfield("OBJL", "b12");
288
0
    poFDefn->AddSubfield("RVER", "b12");
289
0
    poFDefn->AddSubfield("RUIN", "b11");
290
291
0
    poModule->AddField(poFDefn);
292
293
    /* -------------------------------------------------------------------- */
294
    /*      Create the FOID field.                                          */
295
    /* -------------------------------------------------------------------- */
296
0
    poFDefn = new DDFFieldDefn();
297
298
0
    poFDefn->Create("FOID", "Feature object identifier field", "", dsc_vector,
299
0
                    dtc_mixed_data_type);
300
301
0
    poFDefn->AddSubfield("AGEN", "b12");
302
0
    poFDefn->AddSubfield("FIDN", "b14");
303
0
    poFDefn->AddSubfield("FIDS", "b12");
304
305
0
    poModule->AddField(poFDefn);
306
307
    /* -------------------------------------------------------------------- */
308
    /*      Create the ATTF field.                                          */
309
    /* -------------------------------------------------------------------- */
310
0
    poFDefn = new DDFFieldDefn();
311
312
0
    poFDefn->Create("ATTF", "Feature record attribute field", "*", dsc_array,
313
0
                    dtc_mixed_data_type);
314
315
0
    poFDefn->AddSubfield("ATTL", "b12");
316
0
    poFDefn->AddSubfield("ATVL", "A");
317
318
0
    poModule->AddField(poFDefn);
319
320
    /* -------------------------------------------------------------------- */
321
    /*      Create the NATF field.                                          */
322
    /* -------------------------------------------------------------------- */
323
0
    poFDefn = new DDFFieldDefn();
324
325
0
    poFDefn->Create("NATF", "Feature record national attribute field", "*",
326
0
                    dsc_array, dtc_mixed_data_type);
327
328
0
    poFDefn->AddSubfield("ATTL", "b12");
329
0
    poFDefn->AddSubfield("ATVL", "A");
330
331
0
    poModule->AddField(poFDefn);
332
333
    /* -------------------------------------------------------------------- */
334
    /*      Create the FFPC field.                                          */
335
    /* -------------------------------------------------------------------- */
336
0
    poFDefn = new DDFFieldDefn();
337
338
0
    poFDefn->Create("FFPC",
339
0
                    "Feature record to feature object pointer control field",
340
0
                    "", dsc_vector, dtc_mixed_data_type);
341
342
0
    poFDefn->AddSubfield("FFUI", "b11");
343
0
    poFDefn->AddSubfield("FFIX", "b12");
344
0
    poFDefn->AddSubfield("NFPT", "b12");
345
346
0
    poModule->AddField(poFDefn);
347
348
    /* -------------------------------------------------------------------- */
349
    /*      Create the FFPT field.                                          */
350
    /* -------------------------------------------------------------------- */
351
0
    poFDefn = new DDFFieldDefn();
352
353
0
    poFDefn->Create("FFPT", "Feature record to feature object pointer field",
354
0
                    "*", dsc_array, dtc_mixed_data_type);
355
356
0
    poFDefn->AddSubfield("LNAM", "B(64)");
357
0
    poFDefn->AddSubfield("RIND", "b11");
358
0
    poFDefn->AddSubfield("COMT", "A");
359
360
0
    poModule->AddField(poFDefn);
361
362
    /* -------------------------------------------------------------------- */
363
    /*      Create the FSPC field.                                          */
364
    /* -------------------------------------------------------------------- */
365
0
    poFDefn = new DDFFieldDefn();
366
367
0
    poFDefn->Create("FSPC",
368
0
                    "Feature record to spatial record pointer control field",
369
0
                    "", dsc_vector, dtc_mixed_data_type);
370
371
0
    poFDefn->AddSubfield("FSUI", "b11");
372
0
    poFDefn->AddSubfield("FSIX", "b12");
373
0
    poFDefn->AddSubfield("NSPT", "b12");
374
375
0
    poModule->AddField(poFDefn);
376
377
    /* -------------------------------------------------------------------- */
378
    /*      Create the FSPT field.                                          */
379
    /* -------------------------------------------------------------------- */
380
0
    poFDefn = new DDFFieldDefn();
381
382
0
    poFDefn->Create("FSPT", "Feature record to spatial record pointer field",
383
0
                    "*", dsc_array, dtc_mixed_data_type);
384
385
0
    poFDefn->AddSubfield("NAME", "B(40)");
386
0
    poFDefn->AddSubfield("ORNT", "b11");
387
0
    poFDefn->AddSubfield("USAG", "b11");
388
0
    poFDefn->AddSubfield("MASK", "b11");
389
390
0
    poModule->AddField(poFDefn);
391
392
    /* -------------------------------------------------------------------- */
393
    /*      Create file.                                                    */
394
    /* -------------------------------------------------------------------- */
395
0
    if (!poModule->Create(pszFilename))
396
0
    {
397
0
        delete poModule;
398
0
        poModule = nullptr;
399
0
        return false;
400
0
    }
401
402
0
    return true;
403
0
}
404
405
/************************************************************************/
406
/*                             WriteDSID()                              */
407
/************************************************************************/
408
409
bool S57Writer::WriteDSID(int nEXPP, int nINTU, const char *pszDSNM,
410
                          const char *pszEDTN, const char *pszUPDN,
411
                          const char *pszUADT, const char *pszISDT,
412
                          const char *pszSTED, int nAGEN, const char *pszCOMT,
413
                          int nAALL, int nNALL, int nNOMR, int nNOGR, int nNOLR,
414
                          int nNOIN, int nNOCN, int nNOED)
415
416
0
{
417
    /* -------------------------------------------------------------------- */
418
    /*      Default values.                                                 */
419
    /* -------------------------------------------------------------------- */
420
0
    if (pszDSNM == nullptr)
421
0
        pszDSNM = "";
422
0
    if (pszEDTN == nullptr)
423
0
        pszEDTN = "2";
424
0
    if (pszUPDN == nullptr)
425
0
        pszUPDN = "0";
426
0
    if (pszISDT == nullptr)
427
0
        pszISDT = "20030801";
428
0
    if (pszUADT == nullptr)
429
0
        pszUADT = pszISDT;
430
0
    if (pszSTED == nullptr)
431
0
        pszSTED = "03.1";
432
0
    if (pszCOMT == nullptr)
433
0
        pszCOMT = "";
434
435
    /* -------------------------------------------------------------------- */
436
    /*      Add the DSID field.                                             */
437
    /* -------------------------------------------------------------------- */
438
0
    DDFRecord *poRec = MakeRecord();
439
440
    // DDFField *poField =
441
0
    poRec->AddField(poModule->FindFieldDefn("DSID"));
442
443
0
    poRec->SetIntSubfield("DSID", 0, "RCNM", 0, 10);
444
0
    poRec->SetIntSubfield("DSID", 0, "RCID", 0, 1);
445
0
    poRec->SetIntSubfield("DSID", 0, "EXPP", 0, nEXPP);
446
0
    poRec->SetIntSubfield("DSID", 0, "INTU", 0, nINTU);
447
0
    poRec->SetStringSubfield("DSID", 0, "DSNM", 0, pszDSNM);
448
0
    poRec->SetStringSubfield("DSID", 0, "EDTN", 0, pszEDTN);
449
0
    poRec->SetStringSubfield("DSID", 0, "UPDN", 0, pszUPDN);
450
0
    poRec->SetStringSubfield("DSID", 0, "UADT", 0, pszUADT);
451
0
    poRec->SetStringSubfield("DSID", 0, "ISDT", 0, pszISDT);
452
0
    poRec->SetStringSubfield("DSID", 0, "STED", 0, pszSTED);
453
0
    poRec->SetIntSubfield("DSID", 0, "PRSP", 0, 1);
454
0
    poRec->SetStringSubfield("DSID", 0, "PSDN", 0, "");
455
0
    poRec->SetStringSubfield("DSID", 0, "PRED", 0, "2.0");
456
0
    poRec->SetIntSubfield("DSID", 0, "PROF", 0, 1);
457
0
    poRec->SetIntSubfield("DSID", 0, "AGEN", 0, nAGEN);
458
0
    poRec->SetStringSubfield("DSID", 0, "COMT", 0, pszCOMT);
459
460
    /* -------------------------------------------------------------------- */
461
    /*      Add the DSSI record.  Eventually we will need to return and     */
462
    /*      correct these when we are finished writing.                     */
463
    /* -------------------------------------------------------------------- */
464
    /* poField = */ poRec->AddField(poModule->FindFieldDefn("DSSI"));
465
466
0
    poRec->SetIntSubfield("DSSI", 0, "DSTR", 0, 2);  // "Chain node"
467
0
    poRec->SetIntSubfield("DSSI", 0, "AALL", 0, nAALL);
468
0
    poRec->SetIntSubfield("DSSI", 0, "NALL", 0, nNALL);
469
0
    poRec->SetIntSubfield("DSSI", 0, "NOMR", 0, nNOMR);  // Meta records
470
    // Cartographic records are not permitted in ENC.
471
0
    poRec->SetIntSubfield("DSSI", 0, "NOCR", 0, 0);
472
0
    poRec->SetIntSubfield("DSSI", 0, "NOGR", 0, nNOGR);  // Geo records
473
    // Collection records.
474
0
    poRec->SetIntSubfield("DSSI", 0, "NOLR", 0, nNOLR);
475
    // Isolated node records.
476
0
    poRec->SetIntSubfield("DSSI", 0, "NOIN", 0, nNOIN);
477
    // Connected node records.
478
0
    poRec->SetIntSubfield("DSSI", 0, "NOCN", 0, nNOCN);
479
0
    poRec->SetIntSubfield("DSSI", 0, "NOED", 0, nNOED);  // Edge records
480
    // Face are not permitted in chain node structure.
481
0
    poRec->SetIntSubfield("DSSI", 0, "NOFA", 0, 0);
482
483
    /* -------------------------------------------------------------------- */
484
    /*      Write out the record.                                           */
485
    /* -------------------------------------------------------------------- */
486
0
    poRec->Write();
487
0
    delete poRec;
488
489
0
    return true;
490
0
}
491
492
/************************************************************************/
493
/*                             WriteDSPM()                              */
494
/************************************************************************/
495
496
bool S57Writer::WriteDSPM(int nHDAT, int nVDAT, int nSDAT, int nCSCL, int nCOMF,
497
                          int nSOMF)
498
499
0
{
500
0
    m_nCOMF = nCOMF;
501
0
    m_nSOMF = nSOMF;
502
503
    /* -------------------------------------------------------------------- */
504
    /*      Add the DSID field.                                             */
505
    /* -------------------------------------------------------------------- */
506
0
    DDFRecord *poRec = MakeRecord();
507
508
    // DDFField *poField =
509
0
    poRec->AddField(poModule->FindFieldDefn("DSPM"));
510
511
0
    poRec->SetIntSubfield("DSPM", 0, "RCNM", 0, 20);
512
0
    poRec->SetIntSubfield("DSPM", 0, "RCID", 0, 1);
513
    // Must be 2 for ENC.
514
0
    poRec->SetIntSubfield("DSPM", 0, "HDAT", 0, nHDAT);
515
0
    poRec->SetIntSubfield("DSPM", 0, "VDAT", 0, nVDAT);
516
0
    poRec->SetIntSubfield("DSPM", 0, "SDAT", 0, nSDAT);
517
0
    poRec->SetIntSubfield("DSPM", 0, "CSCL", 0, nCSCL);
518
0
    poRec->SetIntSubfield("DSPM", 0, "DUNI", 0, 1);
519
0
    poRec->SetIntSubfield("DSPM", 0, "HUNI", 0, 1);
520
0
    poRec->SetIntSubfield("DSPM", 0, "PUNI", 0, 1);
521
0
    poRec->SetIntSubfield("DSPM", 0, "COUN", 0, 1);
522
0
    poRec->SetIntSubfield("DSPM", 0, "COMF", 0, nCOMF);
523
0
    poRec->SetIntSubfield("DSPM", 0, "SOMF", 0, nSOMF);
524
525
    /* -------------------------------------------------------------------- */
526
    /*      Write out the record.                                           */
527
    /* -------------------------------------------------------------------- */
528
0
    poRec->Write();
529
0
    delete poRec;
530
531
0
    return true;
532
0
}
533
534
/************************************************************************/
535
/*                             MakeRecord()                             */
536
/*                                                                      */
537
/*      Create a new empty record, and append a 0001 field with a       */
538
/*      properly set record index in it.                                */
539
/************************************************************************/
540
541
DDFRecord *S57Writer::MakeRecord()
542
543
0
{
544
0
    unsigned char abyData[2] = {
545
0
        static_cast<unsigned char>(nNext0001Index % 256),
546
0
        static_cast<unsigned char>(nNext0001Index / 256)};
547
548
0
    DDFRecord *poRec = new DDFRecord(poModule);
549
0
    DDFField *poField = poRec->AddField(poModule->FindFieldDefn("0001"));
550
0
    poRec->SetFieldRaw(poField, 0, (const char *)abyData, 2);
551
552
0
    nNext0001Index++;
553
554
0
    return poRec;
555
0
}
556
557
/************************************************************************/
558
/*                           WriteGeometry()                            */
559
/************************************************************************/
560
561
bool S57Writer::WriteGeometry(DDFRecord *poRec, int nVertCount,
562
                              const double *padfX, const double *padfY,
563
                              const double *padfZ)
564
565
0
{
566
0
    const char *pszFieldName = "SG2D";
567
568
0
    if (padfZ != nullptr)
569
0
        pszFieldName = "SG3D";
570
571
0
    DDFField *poField = poRec->AddField(poModule->FindFieldDefn(pszFieldName));
572
573
0
    const int nRawDataSize = padfZ ? 12 * nVertCount : 8 * nVertCount;
574
575
0
    unsigned char *pabyRawData =
576
0
        static_cast<unsigned char *>(CPLMalloc(nRawDataSize));
577
578
0
    for (int i = 0; i < nVertCount; i++)
579
0
    {
580
0
        const GInt32 nXCOO =
581
0
            CPL_LSBWORD32(static_cast<GInt32>(floor(padfX[i] * m_nCOMF + 0.5)));
582
0
        const GInt32 nYCOO =
583
0
            CPL_LSBWORD32(static_cast<GInt32>(floor(padfY[i] * m_nCOMF + 0.5)));
584
585
0
        if (padfZ == nullptr)
586
0
        {
587
0
            memcpy(pabyRawData + i * 8, &nYCOO, 4);
588
0
            memcpy(pabyRawData + i * 8 + 4, &nXCOO, 4);
589
0
        }
590
0
        else
591
0
        {
592
0
            const GInt32 nVE3D = CPL_LSBWORD32(
593
0
                static_cast<GInt32>(floor(padfZ[i] * m_nSOMF + 0.5)));
594
0
            memcpy(pabyRawData + i * 12, &nYCOO, 4);
595
0
            memcpy(pabyRawData + i * 12 + 4, &nXCOO, 4);
596
0
            memcpy(pabyRawData + i * 12 + 8, &nVE3D, 4);
597
0
        }
598
0
    }
599
600
0
    const bool nSuccess = CPL_TO_BOOL(poRec->SetFieldRaw(
601
0
        poField, 0, reinterpret_cast<const char *>(pabyRawData), nRawDataSize));
602
603
0
    CPLFree(pabyRawData);
604
605
0
    return nSuccess;
606
0
}
607
608
/************************************************************************/
609
/*                           WritePrimitive()                           */
610
/************************************************************************/
611
612
bool S57Writer::WritePrimitive(OGRFeature *poFeature)
613
614
0
{
615
0
    DDFRecord *poRec = MakeRecord();
616
0
    const OGRGeometry *poGeom = poFeature->GetGeometryRef();
617
618
    /* -------------------------------------------------------------------- */
619
    /*      Add the VRID field.                                             */
620
    /* -------------------------------------------------------------------- */
621
622
    // DDFField *poField =
623
0
    poRec->AddField(poModule->FindFieldDefn("VRID"));
624
625
0
    poRec->SetIntSubfield("VRID", 0, "RCNM", 0,
626
0
                          poFeature->GetFieldAsInteger("RCNM"));
627
0
    poRec->SetIntSubfield("VRID", 0, "RCID", 0,
628
0
                          poFeature->GetFieldAsInteger("RCID"));
629
0
    poRec->SetIntSubfield("VRID", 0, "RVER", 0, 1);
630
0
    poRec->SetIntSubfield("VRID", 0, "RUIN", 0, 1);
631
632
0
    bool bRet = true;
633
634
    /* -------------------------------------------------------------------- */
635
    /*      Handle simple point.                                            */
636
    /* -------------------------------------------------------------------- */
637
0
    if (poGeom != nullptr && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
638
0
    {
639
0
        const OGRPoint *poPoint = poGeom->toPoint();
640
641
0
        CPLAssert(poFeature->GetFieldAsInteger("RCNM") == RCNM_VI ||
642
0
                  poFeature->GetFieldAsInteger("RCNM") == RCNM_VC);
643
644
0
        const double adfX[1] = {poPoint->getX()};
645
0
        const double adfY[1] = {poPoint->getY()};
646
0
        const double adfZ[1] = {poPoint->getZ()};
647
648
0
        if (adfZ[0] == 0.0)
649
0
            bRet = WriteGeometry(poRec, 1, &adfX[0], &adfY[0], nullptr);
650
0
        else
651
0
            bRet = WriteGeometry(poRec, 1, &adfX[0], &adfY[0], &adfZ[0]);
652
0
    }
653
654
    /* -------------------------------------------------------------------- */
655
    /*      For multipoints we assuming SOUNDG, and write out as SG3D.      */
656
    /* -------------------------------------------------------------------- */
657
0
    else if (poGeom != nullptr &&
658
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
659
0
    {
660
0
        const OGRMultiPoint *poMP = poGeom->toMultiPoint();
661
0
        const int nVCount = poMP->getNumGeometries();
662
663
0
        CPLAssert(poFeature->GetFieldAsInteger("RCNM") == RCNM_VI ||
664
0
                  poFeature->GetFieldAsInteger("RCNM") == RCNM_VC);
665
666
0
        double *padfX = (double *)CPLMalloc(sizeof(double) * nVCount);
667
0
        double *padfY = (double *)CPLMalloc(sizeof(double) * nVCount);
668
0
        double *padfZ = (double *)CPLMalloc(sizeof(double) * nVCount);
669
670
0
        for (int i = 0; i < nVCount; i++)
671
0
        {
672
0
            const OGRPoint *poPoint = poMP->getGeometryRef(i);
673
0
            padfX[i] = poPoint->getX();
674
0
            padfY[i] = poPoint->getY();
675
0
            padfZ[i] = poPoint->getZ();
676
0
        }
677
678
0
        bRet = WriteGeometry(poRec, nVCount, padfX, padfY, padfZ);
679
680
0
        CPLFree(padfX);
681
0
        CPLFree(padfY);
682
0
        CPLFree(padfZ);
683
0
    }
684
685
    /* -------------------------------------------------------------------- */
686
    /*      Handle LINESTRINGs (edge) geometry.                             */
687
    /* -------------------------------------------------------------------- */
688
0
    else if (poGeom != nullptr &&
689
0
             wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
690
0
    {
691
0
        const OGRLineString *poLS = poGeom->toLineString();
692
0
        const int nVCount = poLS->getNumPoints();
693
694
0
        CPLAssert(poFeature->GetFieldAsInteger("RCNM") == RCNM_VE);
695
696
0
        double *padfX = (double *)CPLMalloc(sizeof(double) * nVCount);
697
0
        double *padfY = (double *)CPLMalloc(sizeof(double) * nVCount);
698
699
0
        for (int i = 0; i < nVCount; i++)
700
0
        {
701
0
            padfX[i] = poLS->getX(i);
702
0
            padfY[i] = poLS->getY(i);
703
0
        }
704
705
0
        if (nVCount)
706
0
            bRet = WriteGeometry(poRec, nVCount, padfX, padfY, nullptr);
707
708
0
        CPLFree(padfX);
709
0
        CPLFree(padfY);
710
0
    }
711
712
    /* -------------------------------------------------------------------- */
713
    /*      edge node linkages.                                             */
714
    /* -------------------------------------------------------------------- */
715
0
    if (poFeature->GetDefnRef()->GetFieldIndex("NAME_RCNM_0") >= 0)
716
0
    {
717
0
        CPLAssert(poFeature->GetFieldAsInteger("NAME_RCNM_0") == RCNM_VC);
718
719
        // DDFField *poField =
720
0
        poRec->AddField(poModule->FindFieldDefn("VRPT"));
721
722
0
        const int nRCID0 = poFeature->GetFieldAsInteger("NAME_RCID_0");
723
0
        char szName0[5] = {RCNM_VC, static_cast<char>(nRCID0 & 0xff),
724
0
                           static_cast<char>((nRCID0 & 0xff00) >> 8),
725
0
                           static_cast<char>((nRCID0 & 0xff0000) >> 16),
726
0
                           static_cast<char>((nRCID0 & 0xff000000) >> 24)};
727
728
0
        poRec->SetStringSubfield("VRPT", 0, "NAME", 0, szName0, 5);
729
0
        poRec->SetIntSubfield("VRPT", 0, "ORNT", 0,
730
0
                              poFeature->GetFieldAsInteger("ORNT_0"));
731
0
        poRec->SetIntSubfield("VRPT", 0, "USAG", 0,
732
0
                              poFeature->GetFieldAsInteger("USAG_0"));
733
0
        poRec->SetIntSubfield("VRPT", 0, "TOPI", 0,
734
0
                              poFeature->GetFieldAsInteger("TOPI_0"));
735
0
        poRec->SetIntSubfield("VRPT", 0, "MASK", 0,
736
0
                              poFeature->GetFieldAsInteger("MASK_0"));
737
738
0
        const int nRCID1 = poFeature->GetFieldAsInteger("NAME_RCID_1");
739
0
        const char szName1[5] = {
740
0
            RCNM_VC, static_cast<char>(nRCID1 & 0xff),
741
0
            static_cast<char>((nRCID1 & 0xff00) >> 8),
742
0
            static_cast<char>((nRCID1 & 0xff0000) >> 16),
743
0
            static_cast<char>((nRCID1 & 0xff000000) >> 24)};
744
745
0
        poRec->SetStringSubfield("VRPT", 0, "NAME", 1, szName1, 5);
746
0
        poRec->SetIntSubfield("VRPT", 0, "ORNT", 1,
747
0
                              poFeature->GetFieldAsInteger("ORNT_1"));
748
0
        poRec->SetIntSubfield("VRPT", 0, "USAG", 1,
749
0
                              poFeature->GetFieldAsInteger("USAG_1"));
750
0
        poRec->SetIntSubfield("VRPT", 0, "TOPI", 1,
751
0
                              poFeature->GetFieldAsInteger("TOPI_1"));
752
0
        poRec->SetIntSubfield("VRPT", 0, "MASK", 1,
753
0
                              poFeature->GetFieldAsInteger("MASK_1"));
754
0
    }
755
756
    /* -------------------------------------------------------------------- */
757
    /*      Write out the record.                                           */
758
    /* -------------------------------------------------------------------- */
759
0
    if (!poRec->Write())
760
0
        bRet = false;
761
0
    delete poRec;
762
763
0
    return bRet;
764
0
}
765
766
/************************************************************************/
767
/*                             GetHEXChar()                             */
768
/************************************************************************/
769
770
static char GetHEXChar(const char *pszSrcHEXString)
771
772
0
{
773
0
    if (pszSrcHEXString[0] == '\0' || pszSrcHEXString[1] == '\0')
774
0
        return (char)0;
775
776
0
    int nResult = 0;
777
778
0
    if (pszSrcHEXString[0] >= '0' && pszSrcHEXString[0] <= '9')
779
0
        nResult += (pszSrcHEXString[0] - '0') * 16;
780
0
    else if (pszSrcHEXString[0] >= 'a' && pszSrcHEXString[0] <= 'f')
781
0
        nResult += (pszSrcHEXString[0] - 'a' + 10) * 16;
782
0
    else if (pszSrcHEXString[0] >= 'A' && pszSrcHEXString[0] <= 'F')
783
0
        nResult += (pszSrcHEXString[0] - 'A' + 10) * 16;
784
785
0
    if (pszSrcHEXString[1] >= '0' && pszSrcHEXString[1] <= '9')
786
0
        nResult += pszSrcHEXString[1] - '0';
787
0
    else if (pszSrcHEXString[1] >= 'a' && pszSrcHEXString[1] <= 'f')
788
0
        nResult += pszSrcHEXString[1] - 'a' + 10;
789
0
    else if (pszSrcHEXString[1] >= 'A' && pszSrcHEXString[1] <= 'F')
790
0
        nResult += pszSrcHEXString[1] - 'A' + 10;
791
792
0
    return (char)nResult;
793
0
}
794
795
/************************************************************************/
796
/*                        WriteCompleteFeature()                        */
797
/************************************************************************/
798
799
bool S57Writer::WriteCompleteFeature(OGRFeature *poFeature)
800
801
0
{
802
0
    const OGRFeatureDefn *poFDefn = poFeature->GetDefnRef();
803
804
    /* -------------------------------------------------------------------- */
805
    /*      We handle primitives in a separate method.                      */
806
    /* -------------------------------------------------------------------- */
807
0
    if (EQUAL(poFDefn->GetName(), OGRN_VI) ||
808
0
        EQUAL(poFDefn->GetName(), OGRN_VC) ||
809
0
        EQUAL(poFDefn->GetName(), OGRN_VE))
810
0
        return WritePrimitive(poFeature);
811
812
    /* -------------------------------------------------------------------- */
813
    /*      Create the record.                                              */
814
    /* -------------------------------------------------------------------- */
815
0
    DDFRecord *poRec = MakeRecord();
816
817
    /* -------------------------------------------------------------------- */
818
    /*      Add the FRID.                                                   */
819
    /* -------------------------------------------------------------------- */
820
    // DDFField *poField =
821
0
    poRec->AddField(poModule->FindFieldDefn("FRID"));
822
823
0
    poRec->SetIntSubfield("FRID", 0, "RCNM", 0, 100);
824
0
    poRec->SetIntSubfield("FRID", 0, "RCID", 0,
825
0
                          poFeature->GetFieldAsInteger("RCID"));
826
0
    poRec->SetIntSubfield("FRID", 0, "PRIM", 0,
827
0
                          poFeature->GetFieldAsInteger("PRIM"));
828
0
    poRec->SetIntSubfield("FRID", 0, "GRUP", 0,
829
0
                          poFeature->GetFieldAsInteger("GRUP"));
830
0
    poRec->SetIntSubfield("FRID", 0, "OBJL", 0,
831
0
                          poFeature->GetFieldAsInteger("OBJL"));
832
0
    poRec->SetIntSubfield("FRID", 0, "RVER", 0, 1); /* always new insert*/
833
0
    poRec->SetIntSubfield("FRID", 0, "RUIN", 0, 1);
834
835
    /* -------------------------------------------------------------------- */
836
    /*      Add the FOID                                                    */
837
    /* -------------------------------------------------------------------- */
838
    /*poField = */ poRec->AddField(poModule->FindFieldDefn("FOID"));
839
840
0
    poRec->SetIntSubfield("FOID", 0, "AGEN", 0,
841
0
                          poFeature->GetFieldAsInteger("AGEN"));
842
0
    poRec->SetIntSubfield("FOID", 0, "FIDN", 0,
843
0
                          poFeature->GetFieldAsInteger("FIDN"));
844
0
    poRec->SetIntSubfield("FOID", 0, "FIDS", 0,
845
0
                          poFeature->GetFieldAsInteger("FIDS"));
846
847
    /* -------------------------------------------------------------------- */
848
    /*      ATTF support.                                                   */
849
    /* -------------------------------------------------------------------- */
850
851
0
    if (poRegistrar != nullptr &&
852
0
        poClassContentExplorer->SelectClass(
853
0
            poFeature->GetDefnRef()->GetName()) &&
854
0
        !WriteATTF(poRec, poFeature))
855
0
    {
856
0
        delete poRec;
857
0
        return false;
858
0
    }
859
860
    /* -------------------------------------------------------------------- */
861
    /*      Add the FSPT if needed.                                         */
862
    /* -------------------------------------------------------------------- */
863
0
    if (poFeature->IsFieldSetAndNotNull(poFeature->GetFieldIndex("NAME_RCNM")))
864
0
    {
865
0
        int nItemCount = 0;
866
867
0
        const int *panRCNM =
868
0
            poFeature->GetFieldAsIntegerList("NAME_RCNM", &nItemCount);
869
0
        const int *panRCID =
870
0
            poFeature->GetFieldAsIntegerList("NAME_RCID", &nItemCount);
871
0
        const int *panORNT =
872
0
            poFeature->GetFieldAsIntegerList("ORNT", &nItemCount);
873
0
        const int *panUSAG =
874
0
            poFeature->GetFieldAsIntegerList("USAG", &nItemCount);
875
0
        const int *panMASK =
876
0
            poFeature->GetFieldAsIntegerList("MASK", &nItemCount);
877
878
        // cppcheck-suppress duplicateExpression
879
0
        CPLAssert(sizeof(int) == sizeof(GInt32));
880
881
0
        const int nRawDataSize = nItemCount * 8;
882
0
        unsigned char *pabyRawData = (unsigned char *)CPLMalloc(nRawDataSize);
883
884
0
        for (int i = 0; i < nItemCount; i++)
885
0
        {
886
0
            GInt32 nRCID = CPL_LSBWORD32(panRCID[i]);
887
888
0
            pabyRawData[i * 8 + 0] = (GByte)panRCNM[i];
889
0
            memcpy(pabyRawData + i * 8 + 1, &nRCID, 4);
890
0
            pabyRawData[i * 8 + 5] = (GByte)panORNT[i];
891
0
            pabyRawData[i * 8 + 6] = (GByte)panUSAG[i];
892
0
            pabyRawData[i * 8 + 7] = (GByte)panMASK[i];
893
0
        }
894
895
0
        DDFField *poField = poRec->AddField(poModule->FindFieldDefn("FSPT"));
896
0
        poRec->SetFieldRaw(poField, 0, (const char *)pabyRawData, nRawDataSize);
897
0
        CPLFree(pabyRawData);
898
0
    }
899
900
    /* -------------------------------------------------------------------- */
901
    /*      Add the FFPT if needed.                                         */
902
    /* -------------------------------------------------------------------- */
903
0
    char **papszLNAM_REFS = poFeature->GetFieldAsStringList("LNAM_REFS");
904
905
0
    if (CSLCount(papszLNAM_REFS) > 0)
906
0
    {
907
0
        int i, nRefCount = CSLCount(papszLNAM_REFS);
908
0
        const int *panRIND =
909
0
            poFeature->GetFieldAsIntegerList("FFPT_RIND", nullptr);
910
911
0
        poRec->AddField(poModule->FindFieldDefn("FFPT"));
912
913
0
        for (i = 0; i < nRefCount; i++)
914
0
        {
915
0
            char szLNAM[9];
916
917
0
            if (strlen(papszLNAM_REFS[i]) < 16)
918
0
                continue;
919
920
            // AGEN
921
0
            szLNAM[1] = GetHEXChar(papszLNAM_REFS[i] + 0);
922
0
            szLNAM[0] = GetHEXChar(papszLNAM_REFS[i] + 2);
923
924
            // FIDN
925
0
            szLNAM[5] = GetHEXChar(papszLNAM_REFS[i] + 4);
926
0
            szLNAM[4] = GetHEXChar(papszLNAM_REFS[i] + 6);
927
0
            szLNAM[3] = GetHEXChar(papszLNAM_REFS[i] + 8);
928
0
            szLNAM[2] = GetHEXChar(papszLNAM_REFS[i] + 10);
929
930
            // FIDS
931
0
            szLNAM[7] = GetHEXChar(papszLNAM_REFS[i] + 12);
932
0
            szLNAM[6] = GetHEXChar(papszLNAM_REFS[i] + 14);
933
934
0
            szLNAM[8] = '\0';
935
936
0
            poRec->SetStringSubfield("FFPT", 0, "LNAM", i, (char *)szLNAM, 8);
937
0
            poRec->SetIntSubfield("FFPT", 0, "RIND", i, panRIND[i]);
938
0
        }
939
0
    }
940
941
    /* -------------------------------------------------------------------- */
942
    /*      Write out the record.                                           */
943
    /* -------------------------------------------------------------------- */
944
0
    poRec->Write();
945
0
    delete poRec;
946
947
0
    return true;
948
0
}
949
950
/************************************************************************/
951
/*                           SetClassBased()                            */
952
/************************************************************************/
953
954
void S57Writer::SetClassBased(S57ClassRegistrar *poReg,
955
                              S57ClassContentExplorer *poClassContentExplorerIn)
956
957
0
{
958
0
    poRegistrar = poReg;
959
0
    poClassContentExplorer = poClassContentExplorerIn;
960
0
}
961
962
/************************************************************************/
963
/*                             WriteATTF()                              */
964
/************************************************************************/
965
966
bool S57Writer::WriteATTF(DDFRecord *poRec, OGRFeature *poFeature)
967
0
{
968
0
    CPLAssert(poRegistrar != nullptr);
969
970
    /* -------------------------------------------------------------------- */
971
    /*      Loop over all attributes.                                       */
972
    /* -------------------------------------------------------------------- */
973
0
    int nRawSize = 0;
974
0
    int nACount = 0;
975
0
    char achRawData[5000] = {};
976
977
0
    char **papszAttrList = poClassContentExplorer->GetAttributeList(nullptr);
978
979
0
    for (int iAttr = 0; papszAttrList[iAttr] != nullptr; iAttr++)
980
0
    {
981
0
        const int iField = poFeature->GetFieldIndex(papszAttrList[iAttr]);
982
0
        if (iField < 0)
983
0
            continue;
984
985
0
        const OGRFieldType eFldType =
986
0
            poFeature->GetDefnRef()->GetFieldDefn(iField)->GetType();
987
988
0
        if (!poFeature->IsFieldSetAndNotNull(iField))
989
0
            continue;
990
991
0
        const int nATTLInt =
992
0
            poRegistrar->FindAttrByAcronym(papszAttrList[iAttr]);
993
0
        if (nATTLInt == -1)
994
0
            continue;
995
996
0
        GUInt16 nATTL = (GUInt16)nATTLInt;
997
0
        CPL_LSBPTR16(&nATTL);
998
0
        memcpy(achRawData + nRawSize, &nATTL, 2);
999
0
        nRawSize += 2;
1000
1001
0
        CPLString osATVL;
1002
0
        if (eFldType == OFTStringList)
1003
0
        {
1004
0
            const char *const *papszTokens =
1005
0
                poFeature->GetFieldAsStringList(iField);
1006
0
            for (auto papszIter = papszTokens; papszIter && *papszIter;
1007
0
                 ++papszIter)
1008
0
            {
1009
0
                if (!osATVL.empty())
1010
0
                    osATVL += ',';
1011
0
                osATVL += *papszIter;
1012
0
            }
1013
0
        }
1014
0
        else
1015
0
        {
1016
0
            osATVL = poFeature->GetFieldAsString(iField);
1017
0
        }
1018
1019
        // Special hack to handle special "empty" marker in integer fields.
1020
0
        if ((eFldType == OFTInteger || eFldType == OFTReal) &&
1021
0
            atoi(osATVL) == EMPTY_NUMBER_MARKER)
1022
0
            osATVL.clear();
1023
1024
        // Watch for really long data.
1025
0
        if (osATVL.size() + nRawSize + 10 > sizeof(achRawData))
1026
0
        {
1027
0
            CPLError(CE_Failure, CPLE_AppDefined,
1028
0
                     "Too much ATTF data for fixed buffer size.");
1029
0
            return false;
1030
0
        }
1031
1032
        // copy data into record buffer.
1033
0
        if (!osATVL.empty())
1034
0
        {
1035
0
            memcpy(achRawData + nRawSize, osATVL.data(), osATVL.size());
1036
0
            nRawSize += static_cast<int>(osATVL.size());
1037
0
        }
1038
0
        achRawData[nRawSize++] = DDF_UNIT_TERMINATOR;
1039
1040
0
        nACount++;
1041
0
    }
1042
1043
    /* -------------------------------------------------------------------- */
1044
    /*      If we got no attributes, return without adding ATTF.            */
1045
    /* -------------------------------------------------------------------- */
1046
0
    if (nACount == 0)
1047
0
        return true;
1048
1049
    /* -------------------------------------------------------------------- */
1050
    /*      Write the new field value.                                      */
1051
    /* -------------------------------------------------------------------- */
1052
0
    DDFField *poField = poRec->AddField(poModule->FindFieldDefn("ATTF"));
1053
1054
0
    return CPL_TO_BOOL(poRec->SetFieldRaw(poField, 0, achRawData, nRawSize));
1055
0
}