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