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