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