LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s57 - s57reader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1020 1512 67.5 %
Date: 2026-04-19 18:43:50 Functions: 34 38 89.5 %

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

Generated by: LCOV version 1.14