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

Generated by: LCOV version 1.14