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