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