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