LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s57 - s57reader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 948 1546 61.3 %
Date: 2024-04-28 23:18:46 Functions: 33 38 86.8 %

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

Generated by: LCOV version 1.14