LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101reader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 185 205 90.2 %
Date: 2026-05-08 18:52:02 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  S-101 driver
       4             :  * Purpose:  Implements OGRS101Reader
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_s101.h"
      14             : #include "ogrs101readerconstants.h"
      15             : #include "ogrs101featurecatalog.h"
      16             : 
      17             : #include "cpl_enumerate.h"
      18             : 
      19             : #include <limits>
      20             : #include <memory>
      21             : #include <utility>
      22             : 
      23             : /************************************************************************/
      24             : /*                           OGRS101Reader()                            */
      25             : /************************************************************************/
      26             : 
      27             : OGRS101Reader::OGRS101Reader() = default;
      28             : 
      29             : /************************************************************************/
      30             : /*                           ~OGRS101Reader()                           */
      31             : /************************************************************************/
      32             : 
      33             : OGRS101Reader::~OGRS101Reader() = default;
      34             : 
      35             : /************************************************************************/
      36             : /*                         EmitErrorOrWarning()                         */
      37             : /************************************************************************/
      38             : 
      39             : /*static */ bool
      40         470 : OGRS101Reader::EmitErrorOrWarning(const char *pszFile, const char *pszFunc,
      41             :                                   int nLine, const char *pszMsg, bool bError,
      42             :                                   bool bRecoverable)
      43             : {
      44             : #ifdef _WIN32
      45             :     const char *lastPathSep = strrchr(pszFile, '\\');
      46             : #else
      47         470 :     const char *lastPathSep = strrchr(pszFile, '/');
      48             : #endif
      49         470 :     if (lastPathSep)
      50         470 :         pszFile = lastPathSep + 1;
      51             : 
      52         470 :     if (bError)
      53             :     {
      54         164 :         if (bRecoverable)
      55             :         {
      56         147 :             CPLError(
      57             :                 CE_Failure, CPLE_AppDefined,
      58             :                 "at %s:%d (%s()): %s\n"
      59             :                 "You can potentially try to overcome this error by setting "
      60             :                 "the STRICT open option to FALSE",
      61             :                 pszFile, nLine, pszFunc, pszMsg);
      62             :         }
      63             :         else
      64             :         {
      65          17 :             CPLError(CE_Failure, CPLE_AppDefined, "at %s:%d (%s()): %s",
      66             :                      pszFile, nLine, pszFunc, pszMsg);
      67             :         }
      68             :     }
      69             :     else
      70             :     {
      71         306 :         CPLError(CE_Warning, CPLE_AppDefined, "at %s:%d (%s()): %s", pszFile,
      72             :                  nLine, pszFunc, pszMsg);
      73             :     }
      74         470 :     return !bError;
      75             : }
      76             : 
      77             : /************************************************************************/
      78             : /*                                Load()                                */
      79             : /************************************************************************/
      80             : 
      81             : /** Load a dataset.
      82             :  *
      83             :  * Ingests records in the various DDFRecordIndex members and build layer
      84             :  * definitions.
      85             :  */
      86         355 : bool OGRS101Reader::Load(GDALOpenInfo *poOpenInfo)
      87             : {
      88         355 :     if (!poOpenInfo->fpL)
      89           0 :         return false;
      90             : 
      91         355 :     m_bStrict = CPLTestBool(
      92         355 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
      93         355 :     CPLAssert(!m_poModule);
      94         355 :     m_poModule = std::make_unique<DDFModule>();
      95         355 :     m_osFilename = poOpenInfo->pszFilename;
      96         355 :     VSILFILE *fp = poOpenInfo->fpL;
      97         355 :     poOpenInfo->fpL = nullptr;
      98         355 :     if (!m_poModule->Open(m_osFilename.c_str(), false, fp))
      99           0 :         return false;
     100             : 
     101         355 :     if (!m_poModule->FindFieldDefn(DSID_FIELD))
     102             :     {
     103           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     104             :                  "%s is an ISO8211 file, but not a S-101 data file.",
     105             :                  m_osFilename.c_str());
     106           0 :         return false;
     107             :     }
     108             : 
     109         355 :     DDFRecord *poRecord = m_poModule->ReadRecord();
     110         355 :     if (!ReadDatasetGeneralInformationRecord(poRecord))
     111          48 :         return false;
     112             : 
     113         307 :     poRecord = m_poModule->ReadRecord();
     114         307 :     if (!poRecord)
     115           0 :         return EMIT_ERROR("no Dataset Coordinate Reference System record.");
     116         307 :     bool bSkipFirstReadRecord = false;
     117         307 :     if (!m_bStrict && !poRecord->FindField(CSID_FIELD))
     118             :     {
     119           1 :         EMIT_ERROR_OR_WARNING("CSID field not found.");
     120           1 :         bSkipFirstReadRecord = true;
     121             :     }
     122         306 :     else if (!ReadCSID(poRecord))
     123          29 :         return false;
     124             : 
     125         278 :     return IngestRecords(bSkipFirstReadRecord ? poRecord : nullptr) &&
     126         266 :            ReadFeatureCatalog() && CreateInformationTypeFeatureDefn() &&
     127         257 :            CreatePointFeatureDefns() && CreateMultiPointFeatureDefns() &&
     128         249 :            CreateCurveFeatureDefn() && CreateCompositeCurveFeatureDefn() &&
     129         544 :            CreateSurfaceFeatureDefn() && CreateFeatureTypeFeatureDefns();
     130             : }
     131             : 
     132             : /************************************************************************/
     133             : /*                         ReadFeatureCatalog()                         */
     134             : /************************************************************************/
     135             : 
     136         266 : bool OGRS101Reader::ReadFeatureCatalog()
     137             : {
     138             :     OGRS101FeatureCatalog::LoadingStatus status;
     139         266 :     std::tie(status, m_poFeatureCatalog) =
     140         532 :         OGRS101FeatureCatalog::GetSingletonFeatureCatalog(m_bStrict);
     141         266 :     return status != OGRS101FeatureCatalog::LoadingStatus::ERROR;
     142             : }
     143             : 
     144             : /************************************************************************/
     145             : /*                           IngestRecords()                            */
     146             : /************************************************************************/
     147             : 
     148             : /** Ingest records into the various m_oXXXXXIndex members
     149             :  *
     150             :  * @param poRecordIn Already read record, or nullptr to advance to the next one
     151             :  */
     152         278 : bool OGRS101Reader::IngestRecords(const DDFRecord *poRecordIn)
     153             : {
     154             :     // Must NOT be set as static, as it depends on "this" !
     155             :     /* NOT STATIC */ const struct
     156             :     {
     157             :         const char *pszFieldName;
     158             :         const char *pszType;
     159             :         RecordName nRCNM;
     160             :         int nExpectedCount;
     161             :         DDFRecordIndex &oIndex;
     162         278 :     } asRecordTypeDesc[] = {
     163             :         {IRID_FIELD, "information type", RECORD_NAME_INFORMATION_TYPE,
     164         278 :          m_nCountInformationRecord, m_oInformationTypeRecordIndex},
     165         278 :         {PRID_FIELD, "point", RECORD_NAME_POINT, m_nCountPointRecord,
     166         278 :          m_oPointRecordIndex},
     167             :         {MRID_FIELD, "multipoint", RECORD_NAME_MULTIPOINT,
     168         278 :          m_nCountMultiPointRecord, m_oMultiPointRecordIndex},
     169         278 :         {CRID_FIELD, "curve", RECORD_NAME_CURVE, m_nCountCurveRecord,
     170         278 :          m_oCurveRecordIndex},
     171             :         {CCID_FIELD, "composite curve", RECORD_NAME_COMPOSITE_CURVE,
     172         278 :          m_nCountCompositeCurveRecord, m_oCompositeCurveRecordIndex},
     173         278 :         {SRID_FIELD, "surface", RECORD_NAME_SURFACE, m_nCountSurfaceRecord,
     174         278 :          m_oSurfaceRecordIndex},
     175             :         {FRID_FIELD, "feature type", RECORD_NAME_FEATURE_TYPE,
     176         278 :          m_nCountFeatureTypeRecord, m_oFeatureTypeRecordIndex},
     177         278 :     };
     178             : 
     179         278 :     constexpr int STAR = std::numeric_limits<int>::max();
     180             :     using NameOccMinOccMax = std::tuple<const char *, int, int>;
     181             :     const std::map<RecordName, std::vector<std::vector<NameOccMinOccMax>>>
     182             :         mapExpectedFields = {
     183             :             {
     184             :                 RECORD_NAME_INFORMATION_TYPE,
     185             :                 {{{IRID_FIELD, 1, 1},
     186             :                   {ATTR_FIELD, 0, STAR},
     187             :                   {INAS_FIELD, 0, STAR}}},
     188             :             },
     189             :             {RECORD_NAME_POINT,
     190             :              {
     191             :                  {{{PRID_FIELD, 1, 1},
     192             :                    {INAS_FIELD, 0, STAR},
     193             :                    {C2IT_FIELD, 1, 1}}},
     194             :                  {{{PRID_FIELD, 1, 1},
     195             :                    {INAS_FIELD, 0, STAR},
     196             :                    {C3IT_FIELD, 1, 1}}},
     197             :              }},
     198             :             {RECORD_NAME_MULTIPOINT,
     199             :              {
     200             :                  {{{MRID_FIELD, 1, 1},
     201             :                    {INAS_FIELD, 0, STAR},
     202             :                    {C2IL_FIELD, 1, STAR}}},
     203             :                  {{{MRID_FIELD, 1, 1},
     204             :                    {INAS_FIELD, 0, STAR},
     205             :                    {C3IL_FIELD, 1, STAR}}},
     206             :              }},
     207             :             {RECORD_NAME_CURVE,
     208             :              {
     209             :                  {{{CRID_FIELD, 1, 1},
     210             :                    {INAS_FIELD, 0, STAR},
     211             :                    {PTAS_FIELD, 1, 1},
     212             :                    {SEGH_FIELD, 1, 1},
     213             :                    {C2IL_FIELD, 1, STAR}}},
     214             :              }},
     215             :             {RECORD_NAME_COMPOSITE_CURVE,
     216             :              {
     217             :                  {{{CCID_FIELD, 1, 1},
     218             :                    {INAS_FIELD, 0, STAR},
     219             :                    {CUCO_FIELD, 1, STAR}}},
     220             :              }},
     221             :             {RECORD_NAME_SURFACE,
     222             :              {
     223             :                  {{{SRID_FIELD, 1, 1},
     224             :                    {INAS_FIELD, 0, STAR},
     225             :                    {RIAS_FIELD, 1, STAR}}},
     226             :              }},
     227             :             {RECORD_NAME_FEATURE_TYPE,
     228             :              {
     229             :                  {{{FRID_FIELD, 1, 1},
     230             :                    {FOID_FIELD, 1, 1},
     231             :                    {ATTR_FIELD, 0, STAR},
     232             :                    {INAS_FIELD, 0, STAR},
     233             :                    {SPAS_FIELD, 0, STAR},
     234             :                    {FASC_FIELD, 0, STAR},
     235             :                    {MASK_FIELD, 0, STAR}}},
     236             :              }},
     237        7506 :         };
     238             : 
     239             :     // Loop through all records (except first two DSID and CRID already parsed)
     240             :     // and dispatch them to the appropriate DDFRecordIndex member variable.
     241         278 :     const DDFRecord *poRecord = nullptr;
     242         278 :     const int iFirstRecord = poRecordIn ? 1 : 2;
     243        1801 :     for (int iRecord = iFirstRecord;
     244        1801 :          poRecordIn || (poRecord = m_poModule->ReadRecord()) != nullptr;
     245             :          ++iRecord)
     246             :     {
     247        1537 :         if (poRecordIn)
     248           1 :             std::swap(poRecord, poRecordIn);
     249             : 
     250        1537 :         const auto poField = poRecord->GetField(0);
     251        1537 :         if (!poField)
     252          11 :             return EMIT_ERROR(
     253             :                 CPLSPrintf("Record index %d without field.", iRecord));
     254        1537 :         const char *pszFieldName = poField->GetFieldDefn()->GetName();
     255             : 
     256             :         // Record name
     257             :         const RecordName nRCNM =
     258        1537 :             poRecord->GetIntSubfield(pszFieldName, 0, RCNM_SUBFIELD, 0);
     259             : 
     260             :         // Record identifier
     261             :         const int nRCID =
     262        1537 :             poRecord->GetIntSubfield(pszFieldName, 0, RCID_SUBFIELD, 0);
     263             : 
     264             :         // Check that the fields found in the record match the expectations
     265             :         // from the spec. That is check there are no missing required field,
     266             :         // no duplicate field or unexpected field.
     267        1537 :         std::vector<std::string> fieldsInRecord;
     268        5996 :         for (const auto &oField : poRecord->GetFields())
     269             :         {
     270        4459 :             fieldsInRecord.push_back(oField.GetFieldDefn()->GetName());
     271             :         }
     272        1537 :         const auto oIter = mapExpectedFields.find(nRCNM);
     273        1537 :         if (oIter != mapExpectedFields.end())
     274             :         {
     275        1536 :             bool bMatch = false;
     276        1625 :             for (const auto &expectedFields : oIter->second)
     277             :             {
     278        1609 :                 bMatch = true;
     279        1609 :                 size_t iExpected = 0;
     280        6222 :                 for (size_t i = 0; bMatch && i < fieldsInRecord.size(); ++i)
     281             :                 {
     282        6246 :                     for (; iExpected < expectedFields.size(); ++iExpected)
     283             :                     {
     284        6246 :                         if (fieldsInRecord[i] ==
     285        6246 :                             std::get<0>(expectedFields[iExpected]))
     286             :                         {
     287             :                             const int nMaxOcc =
     288        4548 :                                 std::get<2>(expectedFields[iExpected]);
     289        4548 :                             if (nMaxOcc == 1)
     290             :                             {
     291        4283 :                                 if (i > 0 &&
     292        1337 :                                     fieldsInRecord[i - 1] == fieldsInRecord[i])
     293             :                                 {
     294           0 :                                     bMatch = false;
     295             :                                 }
     296             :                                 else
     297             :                                 {
     298        2946 :                                     ++iExpected;
     299             :                                 }
     300             :                             }
     301        1988 :                             else if (i + 1 == fieldsInRecord.size() ||
     302         386 :                                      fieldsInRecord[i + 1] != fieldsInRecord[i])
     303             :                             {
     304        1517 :                                 ++iExpected;
     305             :                             }
     306        4548 :                             break;
     307             :                         }
     308             :                         else
     309             :                         {
     310             :                             const int nMinOcc =
     311        1698 :                                 std::get<1>(expectedFields[iExpected]);
     312        1698 :                             if (nMinOcc == 1)
     313             :                             {
     314          67 :                                 bMatch = false;
     315          67 :                                 break;
     316             :                             }
     317             :                         }
     318             :                     }
     319             : 
     320             :                     // If we have reached the end of expected fields but
     321             :                     // there are remaining fields, then there are missing
     322             :                     // compulsory fields.
     323        5563 :                     if (iExpected == expectedFields.size() &&
     324         948 :                         i + 1 < fieldsInRecord.size())
     325             :                     {
     326           2 :                         bMatch = false;
     327           2 :                         break;
     328             :                     }
     329             :                 }
     330             : 
     331             :                 // Skip optional fields after the last one in the record
     332        1609 :                 if (bMatch && iExpected < expectedFields.size())
     333             :                 {
     334        1534 :                     for (; iExpected < expectedFields.size(); ++iExpected)
     335             :                     {
     336             :                         const int nMinOcc =
     337         960 :                             std::get<1>(expectedFields[iExpected]);
     338         960 :                         if (nMinOcc > 0)
     339             :                         {
     340          20 :                             bMatch = false;
     341          20 :                             break;
     342             :                         }
     343             :                     }
     344             :                 }
     345        1609 :                 bMatch = bMatch && iExpected == expectedFields.size();
     346        1609 :                 if (bMatch)
     347        1520 :                     break;
     348             :             }
     349        1552 :             if (!bMatch &&
     350          16 :                 !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     351             :                     "Record index %d, RCNM=%d, RCID=%d: invalid "
     352             :                     "sequence of fields.",
     353             :                     iRecord, static_cast<int>(nRCNM), static_cast<int>(nRCID))))
     354             :             {
     355           8 :                 return false;
     356             :             }
     357             :         }
     358             : 
     359        1529 :         const auto iterRecordTypeDesc = std::find_if(
     360             :             std::begin(asRecordTypeDesc), std::end(asRecordTypeDesc),
     361        6386 :             [pszFieldName](const auto &sRecordTypeDesc)
     362             :             {
     363        6386 :                 return strcmp(pszFieldName, sRecordTypeDesc.pszFieldName) == 0;
     364             :             });
     365        1529 :         if (iterRecordTypeDesc != std::end(asRecordTypeDesc))
     366             :         {
     367        1528 :             const auto &sRecordTypeDesc = *iterRecordTypeDesc;
     368             : 
     369        1528 :             if (nRCNM != sRecordTypeDesc.nRCNM &&
     370           0 :                 !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     371             :                     "Record index %d: invalid value %d for RCNM field of %s.",
     372             :                     iRecord, static_cast<int>(nRCNM), pszFieldName)))
     373             :             {
     374           0 :                 return false;
     375             :             }
     376             : 
     377        1528 :             if (nRCID <= 0)
     378             :             {
     379           6 :                 if (!EMIT_ERROR_OR_WARNING(
     380             :                         CPLSPrintf("Record index %d: invalid value %d for "
     381             :                                    "RCID subfield of %s.",
     382             :                                    iRecord, nRCID, pszFieldName)))
     383             :                 {
     384           3 :                     return false;
     385             :                 }
     386           3 :                 break;
     387             :             }
     388             : 
     389        1522 :             if (sRecordTypeDesc.oIndex.FindRecord(nRCID) &&
     390           0 :                 !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     391             :                     "Record index %d: several %s records have RCID = %d.",
     392             :                     iRecord, pszFieldName, nRCID)))
     393             :             {
     394           0 :                 return false;
     395             :             }
     396        1522 :             sRecordTypeDesc.oIndex.AddRecord(nRCID, poRecord->Clone());
     397             :         }
     398           1 :         else if (!EMIT_ERROR_OR_WARNING(
     399             :                      CPLSPrintf("Record index %d: unknown field name %s.",
     400             :                                 iRecord, pszFieldName)))
     401             :         {
     402           0 :             return false;
     403             :         }
     404             :     }
     405             : 
     406             :     // Check consistency between number of records of each category (information
     407             :     // type, point, etc.) and the number actually found.
     408        2129 :     for (const auto &sRecordTypeDesc : asRecordTypeDesc)
     409             :     {
     410        1863 :         if (sRecordTypeDesc.oIndex.GetCount() !=
     411        1875 :                 sRecordTypeDesc.nExpectedCount &&
     412          12 :             !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     413             :                 "%d %s records mentioned in DSSI field, but %d actually found.",
     414             :                 sRecordTypeDesc.nExpectedCount, sRecordTypeDesc.pszType,
     415             :                 sRecordTypeDesc.oIndex.GetCount())))
     416             :         {
     417           1 :             return false;
     418             :         }
     419             :     }
     420             : 
     421         266 :     return true;
     422             : }
     423             : 
     424             : /************************************************************************/
     425             : /*                FillFeatureWithNonAttrAssocSubfields()                */
     426             : /************************************************************************/
     427             : 
     428             : /** Fill attribute fields of the provided feature with the fixed subfields
     429             :  * of the INAS or FASC field.
     430             :  */
     431        3256 : bool OGRS101Reader::FillFeatureWithNonAttrAssocSubfields(
     432             :     const DDFRecord *poRecord, int iRecord, const char *pszFieldName,
     433             :     OGRFeature &oFeature) const
     434             : {
     435        3256 :     const bool bIsINAS = EQUAL(pszFieldName, INAS_FIELD);
     436             : 
     437        6512 :     const auto apoAssocFields = poRecord->GetFields(pszFieldName);
     438        6512 :     const bool bMultipleAssocs = oFeature.GetDefnRef()->GetFieldIndex(
     439             :                                      bIsINAS ? OGR_FIELD_NAME_REF_INFO_RID
     440        3256 :                                              : OGR_FIELD_NAME_REF_FEAT_RID) < 0;
     441        3256 :     const auto &assocRecord =
     442             :         bIsINAS ? m_oInformationTypeRecordIndex : m_oFeatureTypeRecordIndex;
     443             : 
     444        3856 :     for (const auto &[iField, poField] : cpl::enumerate(apoAssocFields))
     445             :     {
     446             :         const std::string osSuffix =
     447         104 :             bMultipleAssocs ? CPLSPrintf("[%d]", static_cast<int>(iField) + 1)
     448         704 :                             : "";
     449             : 
     450          14 :         const auto GetErrorContext = [poRecord, iRecord]()
     451             :         {
     452           7 :             const auto poIDField = poRecord->GetField(0);
     453           7 :             CPLAssert(poIDField);
     454           7 :             const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
     455           7 :             return CPLSPrintf("Record index=%d of %s", iRecord, pszIDFieldName);
     456         600 :         };
     457             : 
     458             :         const RecordName nRRNM =
     459         600 :             poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, 0);
     460         600 :         const RecordName nExpectedRRNM =
     461         600 :             bIsINAS ? RECORD_NAME_INFORMATION_TYPE : RECORD_NAME_FEATURE_TYPE;
     462         600 :         if (nRRNM != nExpectedRRNM)
     463             :         {
     464           4 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     465             :                     "%s: Invalid value for RRNM subfield of %s field: "
     466             :                     "got %d, expected %d.",
     467             :                     GetErrorContext(), pszFieldName, static_cast<int>(nRRNM),
     468             :                     static_cast<int>(nExpectedRRNM))))
     469             :             {
     470           0 :                 return false;
     471             :             }
     472             :         }
     473             : 
     474         600 :         const int nRRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, 0);
     475         600 :         if (!assocRecord.FindRecord(nRRID))
     476             :         {
     477           1 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     478             :                     "%s: Invalid value %d for RRID subfield of %s field: "
     479             :                     "does not match the record identifier of an existing "
     480             :                     "InformationType record.",
     481             :                     GetErrorContext(), static_cast<int>(nRRID), pszFieldName)))
     482             :             {
     483           0 :                 return false;
     484             :             }
     485             :         }
     486             : 
     487         600 :         if (bIsINAS)
     488             :         {
     489         500 :             oFeature.SetField((OGR_FIELD_NAME_REF_INFO_RID + osSuffix).c_str(),
     490             :                               nRRID);
     491             : 
     492             :             const InfoAssocCode nNIAC =
     493         500 :                 poRecord->GetIntSubfield(poField, NIAC_SUBFIELD, 0);
     494         500 :             const auto iterIAC = m_informationAssociationCodes.find(nNIAC);
     495         500 :             if (iterIAC == m_informationAssociationCodes.end())
     496             :             {
     497           1 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     498             :                         "%s: cannot find attribute code %d in IACS field "
     499             :                         "of the Dataset General Information Record.",
     500             :                         GetErrorContext(), static_cast<int>(nNIAC))))
     501             :                 {
     502           0 :                     return false;
     503             :                 }
     504             :                 else
     505             :                 {
     506           1 :                     oFeature.SetField((OGR_FIELD_NAME_NIAC + osSuffix).c_str(),
     507             :                                       CPLSPrintf("informationAssociationCode%d",
     508             :                                                  static_cast<int>(nNIAC)));
     509             :                 }
     510             :             }
     511             :             else
     512             :             {
     513         499 :                 oFeature.SetField((OGR_FIELD_NAME_NIAC + osSuffix).c_str(),
     514         499 :                                   iterIAC->second.c_str());
     515             :             }
     516             :         }
     517             :         else
     518             :         {
     519         100 :             const auto oIterFID = m_oMapFeatureTypeIdToFDefn.find(nRRID);
     520         100 :             if (oIterFID != m_oMapFeatureTypeIdToFDefn.end())
     521             :             {
     522         100 :                 oFeature.SetField(
     523         200 :                     (OGR_FIELD_NAME_REF_FEAT_LAYER_NAME + osSuffix).c_str(),
     524         100 :                     oIterFID->second->GetName());
     525             :             }
     526             : 
     527         100 :             oFeature.SetField((OGR_FIELD_NAME_REF_FEAT_RID + osSuffix).c_str(),
     528             :                               nRRID);
     529             : 
     530             :             const FeatureAssocCode nNFAC =
     531         100 :                 poRecord->GetIntSubfield(poField, NFAC_SUBFIELD, 0);
     532         100 :             const auto iterFAC = m_featureAssociationCodes.find(nNFAC);
     533         100 :             if (iterFAC == m_featureAssociationCodes.end())
     534             :             {
     535           0 :                 if (!EMIT_ERROR_OR_WARNING(
     536             :                         CPLSPrintf("%s: cannot find feature association code "
     537             :                                    "%d in FACS field "
     538             :                                    "of the Dataset General Information Record.",
     539             :                                    GetErrorContext(), static_cast<int>(nNFAC))))
     540             :                 {
     541           0 :                     return false;
     542             :                 }
     543             :                 else
     544             :                 {
     545           0 :                     oFeature.SetField((OGR_FIELD_NAME_NFAC + osSuffix).c_str(),
     546             :                                       CPLSPrintf("featureAssociationCode%d",
     547             :                                                  static_cast<int>(nNFAC)));
     548             :                 }
     549             :             }
     550             :             else
     551             :             {
     552         100 :                 oFeature.SetField((OGR_FIELD_NAME_NFAC + osSuffix).c_str(),
     553         100 :                                   iterFAC->second.c_str());
     554             :             }
     555             :         }
     556             : 
     557             :         const AssocRoleCode nNARC =
     558         600 :             poRecord->GetIntSubfield(poField, NARC_SUBFIELD, 0);
     559         600 :         const auto iterARC = m_associationRoleCodes.find(nNARC);
     560         600 :         if (iterARC == m_associationRoleCodes.end())
     561             :         {
     562           1 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     563             :                     "%s: cannot find attribute code %d in ARCS field "
     564             :                     "of the Dataset General Information Record.",
     565             :                     GetErrorContext(), static_cast<int>(nNARC))))
     566             :             {
     567           0 :                 return false;
     568             :             }
     569             :             else
     570             :             {
     571           2 :                 oFeature.SetField(((bIsINAS ? OGR_FIELD_NAME_NARC
     572           2 :                                             : OGR_FIELD_NAME_FEATURE_NARC) +
     573             :                                    osSuffix)
     574             :                                       .c_str(),
     575             :                                   CPLSPrintf("associationRoleCode%d",
     576             :                                              static_cast<int>(nNARC)));
     577             :             }
     578             :         }
     579             :         else
     580             :         {
     581        1198 :             oFeature.SetField(
     582        1198 :                 ((bIsINAS ? OGR_FIELD_NAME_NARC : OGR_FIELD_NAME_FEATURE_NARC) +
     583             :                  osSuffix)
     584             :                     .c_str(),
     585         599 :                 iterARC->second.c_str());
     586             :         }
     587             : 
     588         600 :         const char *pszSubFieldName = bIsINAS ? IUIN_SUBFIELD : FAUI_SUBFIELD;
     589             :         const int nInstruction =
     590         600 :             poRecord->GetIntSubfield(poField, pszSubFieldName, 0);
     591         600 :         if (nInstruction != INSTRUCTION_INSERT)
     592             :         {
     593           0 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: wrong value %d for %s "
     594             :                                                   "subfield of %s field.",
     595             :                                                   GetErrorContext(),
     596             :                                                   nInstruction, pszSubFieldName,
     597             :                                                   pszFieldName)))
     598             :             {
     599           0 :                 return false;
     600             :             }
     601             :         }
     602             :     }
     603             : 
     604        3256 :     return true;
     605             : }

Generated by: LCOV version 1.14