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