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