LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101readerdatasetgeneralinformationrecord.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 255 311 82.0 %
Date: 2026-05-08 18:52:02 Functions: 24 24 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             : 
      16             : #include <algorithm>
      17             : #include <array>
      18             : #include <cmath>
      19             : #include <memory>
      20             : #include <set>
      21             : #include <string_view>
      22             : #include <utility>
      23             : 
      24             : /************************************************************************/
      25             : /*                ReadDatasetGeneralInformationRecord()                 */
      26             : /************************************************************************/
      27             : 
      28             : /** Read the general information record */
      29         355 : bool OGRS101Reader::ReadDatasetGeneralInformationRecord(
      30             :     const DDFRecord *poRecord)
      31             : {
      32         355 :     if (!poRecord)
      33           0 :         return EMIT_ERROR("no Dataset General Information record.");
      34             : 
      35         689 :     return ReadDSID(poRecord) && ReadDSSI(poRecord) && ReadATCS(poRecord) &&
      36         307 :            ReadITCS(poRecord) && ReadFTCS(poRecord) && ReadIACS(poRecord) &&
      37         689 :            ReadFACS(poRecord) && ReadARCS(poRecord);
      38             : }
      39             : 
      40             : /************************************************************************/
      41             : /*                              ReadDSID()                              */
      42             : /************************************************************************/
      43             : 
      44             : /** Read the Dataset Identification (DSID) field of the general information
      45             :  * record.
      46             :  */
      47         355 : bool OGRS101Reader::ReadDSID(const DDFRecord *poRecord)
      48             : {
      49         355 :     const auto poField = poRecord->FindField(DSID_FIELD);
      50         355 :     if (!poField)
      51           2 :         return EMIT_ERROR("DSID field not found.");
      52             : 
      53             :     // Record name
      54             :     const RecordName nRCNM =
      55         353 :         poRecord->GetIntSubfield(poField, RCNM_SUBFIELD, 0);
      56         355 :     if (nRCNM != RECORD_NAME_DATASET_IDENTIFICATION &&
      57           2 :         !EMIT_ERROR_OR_WARNING("Invalid value for RCNM subfield of DSID."))
      58             :     {
      59           1 :         return false;
      60             :     }
      61             : 
      62             :     // Record identifier
      63         352 :     const int nRCID = poRecord->GetIntSubfield(poField, RCID_SUBFIELD, 0);
      64             :     // Only one record expected
      65         354 :     if (nRCID != 1 &&
      66           2 :         !EMIT_ERROR_OR_WARNING("Invalid value for RCID subfield of DSID."))
      67             :     {
      68           1 :         return false;
      69             :     }
      70             : 
      71             :     static const struct
      72             :     {
      73             :         const char *pszS101Name;
      74             :         const char *pszGDALName;
      75             :     } mapMetadataKeys[] = {
      76             :         {"ENSP", "ENCODING_SPECIFICATION"},
      77             :         {"ENED", "ENCODING_SPECIFICATION_EDITION"},
      78             :         {"PRSP", "PRODUCT_IDENTIFIER"},
      79             :         {"PRED", "PRODUCT_EDITION"},
      80             :         {"PROF", "APPLICATION_PROFILE"},
      81             :         {"DSNM", "DATASET_IDENTIFIER"},
      82             :         {"DSTL", "DATASET_TITLE"},
      83             :         {"DSRD", "DATASET_REFERENCE_DATE"},
      84             :         {"DSLG", "DATASET_LANGUAGE"},
      85             :         {"DSAB", "DATASET_ABSTRACT"},
      86             :         {"DSED", "DATASET_EDITION"},
      87             :     };
      88             : 
      89        4212 :     for (const auto &item : mapMetadataKeys)
      90             :     {
      91             :         const char *pszValue =
      92        3861 :             poRecord->GetStringSubfield(poField, item.pszS101Name, 0);
      93        3861 :         if (!pszValue)
      94             :         {
      95           0 :             if (!EMIT_ERROR_OR_WARNING(std::string("no subfield ")
      96             :                                            .append(item.pszS101Name)
      97             :                                            .append(" in DSID.")
      98             :                                            .c_str()))
      99             :             {
     100           0 :                 return false;
     101             :             }
     102             :         }
     103        3861 :         else if (pszValue[0])
     104             :         {
     105        3510 :             m_aosMetadata.SetNameValue(item.pszGDALName, pszValue);
     106             :         }
     107             :     }
     108             : 
     109             :     const char *pszPRSP =
     110         351 :         m_aosMetadata.FetchNameValueDef("PRODUCT_IDENTIFIER", "");
     111         351 :     if (!strstr(pszPRSP, "S-101") &&
     112           0 :         !EMIT_ERROR_OR_WARNING(
     113             :             CPLSPrintf("%s is an ISO8211 file, but not a S-101 product. "
     114             :                        "Product identifier is '%s'.",
     115             :                        m_osFilename.c_str(), pszPRSP)))
     116             :     {
     117           0 :         return false;
     118             :     }
     119             : 
     120             :     // Accept 1.x, but only >= 2.0 is operational
     121        1053 :     if (!STARTS_WITH(pszPRSP, "INT.IHO.S-101.1.") &&
     122         353 :         !STARTS_WITH(pszPRSP, "INT.IHO.S-101.2.") &&
     123           2 :         !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     124             :             "Product identifier is '%s', but only 'INT.IHO.S-101.2.0' is "
     125             :             "nominally handled. Going on but the dataset might not be "
     126             :             "correctly read.",
     127             :             pszPRSP)))
     128             :     {
     129           1 :         return false;
     130             :     }
     131             : 
     132         350 :     if (!CheckFieldDefinitions())
     133          16 :         return false;
     134             : 
     135         334 :     return true;
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                       CheckFieldDefinitions()                        */
     140             : /************************************************************************/
     141             : 
     142             : /** Check that field and subfield definitions conforms to the specification.
     143             :  */
     144         350 : bool OGRS101Reader::CheckFieldDefinitions() const
     145             : {
     146         350 :     bool bRet = CheckField0000Definition();
     147             : 
     148             :     // Note the '\\\\' has 4 bytes instead of the '\\' that appears in the
     149             :     // spec, because of the escaping of backslash in C++
     150             :     const std::map<std::string_view, std::pair<const char *, const char *>>
     151             :         fieldArrayDescrAndFieldControls = {
     152             :             {DSID_FIELD,
     153           0 :              {"RCNM!RCID!ENSP!ENED!PRSP!PRED!PROF!DSNM!DSTL!DSRD!DSLG!DSAB!"
     154             :               "DSED\\\\*DSTC",
     155           0 :               "(b11,b14,7A,A(8),3A,(b11))"}},
     156             :             {DSSI_FIELD,
     157           0 :              {"DCOX!DCOY!DCOZ!CMFX!CMFY!CMFZ!NOIR!NOPN!NOMN!NOCN!NOXN!NOSN!"
     158             :               "NOFR",
     159           0 :               "(3b48,10b14)"}},
     160           0 :             {ATCS_FIELD, {"*ATCD!ANCD", "(A,b12)"}},
     161           0 :             {ITCS_FIELD, {"*ITCD!ITNC", "(A,b12)"}},
     162           0 :             {FTCS_FIELD, {"*FTCD!FTNC", "(A,b12)"}},
     163           0 :             {IACS_FIELD, {"*IACD!IANC", "(A,b12)"}},
     164           0 :             {FACS_FIELD, {"*FACD!FANC", "(A,b12)"}},
     165           0 :             {ARCS_FIELD, {"*ARCD!ARNC", "(A,b12)"}},
     166           0 :             {CSID_FIELD, {"RCNM!RCID!NCRC", "(b11,b14,b11)"}},
     167             :             {CRSH_FIELD,
     168           0 :              {"CRIX!CRST!CSTY!CRNM!CRSI!CRSS!SCRI", "(3b11,2A,b11,A)"}},
     169           0 :             {CSAX_FIELD, {"*AXTY!AXUM", "(2b11)"}},
     170           0 :             {VDAT_FIELD, {"DTNM!DTID!DTSR!SCRI", "(2A,b11,A)"}},
     171           0 :             {IRID_FIELD, {"RCNM!RCID!NITC!RVER!RUIN", "(b11,b14,2b12,b11)"}},
     172             :             {INAS_FIELD,
     173           0 :              {"RRNM!RRID!NIAC!NARC!IUIN\\\\*NATC!ATIX!PAIX!ATIN!ATVL",
     174           0 :               "(b11,b14,2b12,b11,(3b12,b11,A))"}},
     175           0 :             {ATTR_FIELD, {"*NATC!ATIX!PAIX!ATIN!ATVL", "(3b12,b11,A)"}},
     176           0 :             {C2IT_FIELD, {"YCOO!XCOO", "(2b24)"}},
     177           0 :             {C3IT_FIELD, {"VCID!YCOO!XCOO!ZCOO", "(b11,3b24)"}},
     178           0 :             {C2IL_FIELD, {"*YCOO!XCOO", "(2b24)"}},
     179           0 :             {C3IL_FIELD, {"VCID\\\\*YCOO!XCOO!ZCOO", "(b11,(3b24))"}},
     180           0 :             {PRID_FIELD, {"RCNM!RCID!RVER!RUIN", "(b11,b14,b12,b11)"}},
     181           0 :             {MRID_FIELD, {"RCNM!RCID!RVER!RUIN", "(b11,b14,b12,b11)"}},
     182           0 :             {CRID_FIELD, {"RCNM!RCID!RVER!RUIN", "(b11,b14,b12,b11)"}},
     183           0 :             {PTAS_FIELD, {"*RRNM!RRID!TOPI", "(b11,b14,b11)"}},
     184           0 :             {SEGH_FIELD, {"INTP", "(b11)"}},
     185           0 :             {CCID_FIELD, {"RCNM!RCID!RVER!RUIN", "(b11,b14,b12,b11)"}},
     186           0 :             {CUCO_FIELD, {"*RRNM!RRID!ORNT", "(b11,b14,b11)"}},
     187           0 :             {SRID_FIELD, {"RCNM!RCID!RVER!RUIN", "(b11,b14,b12,b11)"}},
     188           0 :             {RIAS_FIELD, {"*RRNM!RRID!ORNT!USAG!RAUI", "(b11,b14,3b11)"}},
     189           0 :             {FRID_FIELD, {"RCNM!RCID!NFTC!RVER!RUIN", "(b11,b14,2b12,b11)"}},
     190           0 :             {FOID_FIELD, {"AGEN!FIDN!FIDS", "(b12,b14,b12)"}},
     191             :             {SPAS_FIELD,
     192           0 :              {"*RRNM!RRID!ORNT!SMIN!SMAX!SAUI", "(b11,b14,b11,2b14,b11)"}},
     193             :             {FASC_FIELD,
     194           0 :              {"RRNM!RRID!NFAC!NARC!FAUI\\\\*NATC!ATIX!PAIX!ATIN!ATVL",
     195           0 :               "(b11,b14,2b12,b11,(3b12,b11,A))"}},
     196           0 :             {MASK_FIELD, {"*RRNM!RRID!MIND!MUIN", "(b11,b14,2b11)"}},
     197         350 :         };
     198             : 
     199        5525 :     for (const auto &poFieldDefn : m_poModule->GetFieldDefns())
     200             :     {
     201        5175 :         const char *pszFieldName = poFieldDefn->GetName();
     202        5175 :         if (strcmp(pszFieldName, _0000_FIELD) != 0)
     203             :         {
     204             :             const auto oIter =
     205        4827 :                 fieldArrayDescrAndFieldControls.find(pszFieldName);
     206        4827 :             if (oIter == fieldArrayDescrAndFieldControls.end())
     207             :             {
     208           2 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     209             :                         "Unknown field definition '%s'.", pszFieldName)))
     210             :                 {
     211           1 :                     bRet = false;
     212             :                 }
     213             :             }
     214             :             else
     215             :             {
     216        4825 :                 const char *pszExpectedArrayDescr = oIter->second.first;
     217        4825 :                 if (strcmp(poFieldDefn->GetArrayDescr(),
     218        4827 :                            pszExpectedArrayDescr) != 0 &&
     219           2 :                     !EMIT_ERROR_OR_WARNING(
     220             :                         CPLSPrintf("For array description of field definition "
     221             :                                    "'%s', got '%s' whereas '%s' is expected.",
     222             :                                    pszFieldName, poFieldDefn->GetArrayDescr(),
     223             :                                    pszExpectedArrayDescr)))
     224             :                 {
     225           1 :                     bRet = false;
     226             :                 }
     227             : 
     228        4825 :                 const char *pszExpectedFormatControls = oIter->second.second;
     229        4825 :                 if (strcmp(poFieldDefn->GetFormatControls(),
     230        4827 :                            pszExpectedFormatControls) != 0 &&
     231           2 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     232             :                         "For format controls of field definition '%s', got "
     233             :                         "'%s' whereas '%s' is expected.",
     234             :                         pszFieldName, poFieldDefn->GetFormatControls(),
     235             :                         pszExpectedFormatControls)))
     236             :                 {
     237           1 :                     bRet = false;
     238             :                 }
     239             : 
     240        4825 :                 const DDF_data_struct_code eExpectedDataStruct =
     241        4825 :                     strstr(pszExpectedArrayDescr, "\\\\") != nullptr
     242        9097 :                         ? dsc_concatenated
     243        4272 :                     : pszExpectedArrayDescr[0] == '*' ? dsc_array
     244             :                                                       : dsc_vector;
     245        4825 :                 if (poFieldDefn->GetDataStructCode() != eExpectedDataStruct &&
     246           0 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     247             :                         "Data struct code of field definition '%s', got "
     248             :                         "'%d' whereas '%d' is expected.",
     249             :                         pszFieldName, poFieldDefn->GetDataStructCode(),
     250             :                         eExpectedDataStruct)))
     251             :                 {
     252           0 :                     bRet = false;
     253             :                 }
     254             : 
     255        4825 :                 const bool bHasIntegerSubfields =
     256        5144 :                     strstr(pszExpectedFormatControls, "b1") != nullptr ||
     257         319 :                     strstr(pszExpectedFormatControls, "b2") != nullptr;
     258        4825 :                 const bool bHasFloatSubfields =
     259        4825 :                     strstr(pszExpectedFormatControls, "b4") != nullptr;
     260        4825 :                 const bool bHasStringSubfields =
     261        4825 :                     strchr(pszExpectedFormatControls, 'A') != nullptr;
     262        4825 :                 const DDF_data_type_code eExpectedDataTypeCode =
     263        4825 :                     (bHasIntegerSubfields && !bHasFloatSubfields &&
     264             :                      !bHasStringSubfields)
     265        9650 :                         ? dtc_implicit_point
     266             :                         :
     267             :                         // Commenting below code, since it doesn't actually
     268             :                         // occur with S-101 fields
     269             :                         // (!bHasIntegerSubfields && bHasFloatSubfields && !bHasStringSubfields) ?
     270             :                         //    dtc_explicit_point :
     271             :                         // cppcheck-suppress knownConditionTrueFalse
     272           0 :                         (!bHasIntegerSubfields && !bHasFloatSubfields &&
     273             :                          bHasStringSubfields)
     274        2518 :                         ? dtc_char_string
     275             :                         : dtc_mixed_data_type;
     276        4827 :                 if (poFieldDefn->GetDataTypeCode() != eExpectedDataTypeCode &&
     277           2 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     278             :                         "Data type code of field definition '%s', got "
     279             :                         "'%d' whereas '%d' is expected.",
     280             :                         pszFieldName, poFieldDefn->GetDataTypeCode(),
     281             :                         eExpectedDataTypeCode)))
     282             :                 {
     283           1 :                     bRet = false;
     284             :                 }
     285             :             }
     286             :         }
     287             :     }
     288             : 
     289         700 :     return bRet;
     290             : }
     291             : 
     292             : /************************************************************************/
     293             : /*                      CheckField0000Definition()                      */
     294             : /************************************************************************/
     295             : 
     296             : /** Check that the special "0000" field conforms to the specification.
     297             :  */
     298         350 : bool OGRS101Reader::CheckField0000Definition() const
     299             : {
     300         350 :     bool bRet = true;
     301             : 
     302         350 :     const auto po0000FieldDefn = m_poModule->FindFieldDefn(_0000_FIELD);
     303         350 :     if (!po0000FieldDefn)
     304             :     {
     305           2 :         return EMIT_ERROR_OR_WARNING(
     306             :             "Field definition of 0000 control field not found.");
     307             :     }
     308             : 
     309         350 :     if (po0000FieldDefn->GetDataStructCode() != dsc_elementary &&
     310           2 :         !EMIT_ERROR_OR_WARNING("Data struct code of field definition of 0000 "
     311             :                                "control field must be elementary."))
     312             :     {
     313           1 :         bRet = false;
     314             :     }
     315             : 
     316         350 :     if (po0000FieldDefn->GetDataTypeCode() != dtc_char_string &&
     317           2 :         !EMIT_ERROR_OR_WARNING("Data type code of field definition of 0000 "
     318             :                                "control field must be char_string."))
     319             :     {
     320           1 :         bRet = false;
     321             :     }
     322             : 
     323         352 :     if (po0000FieldDefn->GetFormatControls()[0] != 0 &&
     324           4 :         !EMIT_ERROR_OR_WARNING("Format controls of field definition of 0000 "
     325             :                                "control field must be empty."))
     326             :     {
     327           2 :         bRet = false;
     328             :     }
     329             : 
     330             :     // Should contain concatenated pairs of parent_field_name,child_field_name
     331             :     // without any separator: e.g. DSIDDSSICSIDCRSHCRSHCSAXCRSHVDAT
     332         348 :     const char *psz0000Descr = po0000FieldDefn->GetArrayDescr();
     333         348 :     const size_t n0000DescrLen = strlen(psz0000Descr);
     334         348 :     constexpr int FIELD_NAME_SIZE = 4;
     335         348 :     constexpr int PARENT_CHILD_PAIR_SIZE = 2 * FIELD_NAME_SIZE;
     336         350 :     if ((n0000DescrLen % PARENT_CHILD_PAIR_SIZE) != 0 &&
     337           2 :         !EMIT_ERROR_OR_WARNING(
     338             :             "Length of field tag pairs of field definition of 0000 "
     339             :             "control field must be a multiple of 8."))
     340             :     {
     341           1 :         bRet = false;
     342             :     }
     343             : 
     344         348 :     constexpr int MAX_CHILDREN_COUNT = 8;
     345             :     using ArrayOfChildren = std::array<const char *, MAX_CHILDREN_COUNT>;
     346             : 
     347             :     static const struct
     348             :     {
     349             :         const std::string_view svParent;
     350             :         ArrayOfChildren apszChildren;
     351             :     } knownPairs[] = {
     352             :         {DSID_FIELD,
     353             :          {DSSI_FIELD, ATCS_FIELD, ITCS_FIELD, FTCS_FIELD, IACS_FIELD,
     354             :           FACS_FIELD, ARCS_FIELD}},
     355             :         {CSID_FIELD, {CRSH_FIELD}},
     356             :         {CRSH_FIELD, {CSAX_FIELD, VDAT_FIELD}},
     357             :         {IRID_FIELD, {ATTR_FIELD, INAS_FIELD}},
     358             :         {PRID_FIELD, {INAS_FIELD, C2IT_FIELD, C3IT_FIELD}},
     359             :         {MRID_FIELD, {INAS_FIELD, C2IL_FIELD, C3IL_FIELD}},
     360             :         {CRID_FIELD, {INAS_FIELD, PTAS_FIELD, SEGH_FIELD}},
     361             :         {SEGH_FIELD, {C2IL_FIELD}},
     362             :         {CCID_FIELD, {INAS_FIELD, CUCO_FIELD}},
     363             :         {SRID_FIELD, {INAS_FIELD, RIAS_FIELD}},
     364             :         {FRID_FIELD,
     365             :          {FOID_FIELD, ATTR_FIELD, INAS_FIELD, SPAS_FIELD, FASC_FIELD,
     366             :           MASK_FIELD}},
     367         348 :     };
     368             : 
     369         696 :     const std::set<std::string> oSetUsedFieldNames = [this]
     370             :     {
     371         348 :         std::set<std::string> s;
     372        5511 :         for (const auto &poFieldDefn : m_poModule->GetFieldDefns())
     373        5163 :             s.insert(poFieldDefn->GetName());
     374         348 :         return s;
     375         696 :     }();
     376             : 
     377             :     // Return an iterator of knownPairs that points to the entry where
     378             :     // iter->svParent == svParent, but only if one of its allowed children
     379             :     // is in the set of fields actually found in the file.
     380             :     const auto GetParentIter =
     381       14344 :         [&oSetUsedFieldNames](const std::string_view &svParent)
     382             :     {
     383             :         const auto iter =
     384        8785 :             std::find_if(std::begin(knownPairs), std::end(knownPairs),
     385       58370 :                          [&svParent](const auto &item)
     386       58370 :                          { return svParent == item.svParent; });
     387        8785 :         if (iter != std::end(knownPairs))
     388             :         {
     389        5559 :             const auto allowedChildren = iter->apszChildren;
     390        5559 :             if (std::find_if(allowedChildren.begin(), allowedChildren.end(),
     391       11370 :                              [&oSetUsedFieldNames](const char *pszStr)
     392             :                              {
     393        5718 :                                  return pszStr &&
     394        5652 :                                         cpl::contains(oSetUsedFieldNames,
     395       11436 :                                                       pszStr);
     396        5559 :                              }) != allowedChildren.end())
     397             :             {
     398        5548 :                 return iter;
     399             :             }
     400             :         }
     401        3237 :         return std::end(knownPairs);
     402         348 :     };
     403             : 
     404             :     // Returns an iterator of allowedChildren only is svChild is one of the
     405             :     // knownPairs::apszChildren.
     406       35045 :     const auto IsKnownChild = [](const ArrayOfChildren &allowedChildren,
     407             :                                  const std::string_view &svChild)
     408             :     {
     409       35045 :         return std::find_if(allowedChildren.begin(), allowedChildren.end(),
     410      339405 :                             [&svChild](const char *pszStr)
     411      239662 :                             { return pszStr && svChild == pszStr; }) !=
     412       35045 :                allowedChildren.end();
     413             :     };
     414             : 
     415         348 :     const size_t nPairs = n0000DescrLen / PARENT_CHILD_PAIR_SIZE;
     416         696 :     std::set<std::string> oSetReferencedParent;
     417         696 :     std::set<std::string> oSetReferencedChildren;
     418         348 :     std::set<std::pair<std::string, std::string>> oSetFoundPairs;
     419        4027 :     for (size_t i = 0; i < nPairs; ++i)
     420             :     {
     421        3679 :         bool bUnknownParentOrChild = false;
     422        3679 :         const std::string osParent(psz0000Descr + i * PARENT_CHILD_PAIR_SIZE,
     423        7358 :                                    FIELD_NAME_SIZE);
     424        3679 :         oSetReferencedParent.insert(osParent);
     425             : 
     426        3679 :         const std::string osChild(psz0000Descr + i * PARENT_CHILD_PAIR_SIZE +
     427             :                                       FIELD_NAME_SIZE,
     428        7358 :                                   FIELD_NAME_SIZE);
     429        3679 :         oSetReferencedChildren.insert(osChild);
     430             : 
     431        3681 :         if (!cpl::contains(oSetUsedFieldNames, osParent) &&
     432           2 :             !EMIT_ERROR_OR_WARNING(
     433             :                 CPLSPrintf("Field '%s' referenced in field definition of 0000 "
     434             :                            "control field does not exist.",
     435             :                            osParent.c_str())))
     436             :         {
     437           1 :             bUnknownParentOrChild = true;
     438           1 :             bRet = false;
     439             :         }
     440             : 
     441        3683 :         if (!cpl::contains(oSetUsedFieldNames, osChild) &&
     442           4 :             !EMIT_ERROR_OR_WARNING(
     443             :                 CPLSPrintf("Field '%s' referenced in field definition of 0000 "
     444             :                            "control field does not exist.",
     445             :                            osChild.c_str())))
     446             :         {
     447           2 :             bUnknownParentOrChild = true;
     448           2 :             bRet = false;
     449             :         }
     450             : 
     451        3681 :         if (!oSetFoundPairs.insert({osParent, osChild}).second &&
     452           2 :             !EMIT_ERROR_OR_WARNING(
     453             :                 CPLSPrintf("Pair ('%s','%s') referenced multiple time in field "
     454             :                            "definition of 0000 control field.",
     455             :                            osParent.c_str(), osChild.c_str())))
     456             :         {
     457           1 :             bRet = false;
     458             :         }
     459             : 
     460        3679 :         if (!bUnknownParentOrChild)
     461             :         {
     462        3676 :             const auto oIter = GetParentIter(osParent);
     463        3676 :             if (oIter == std::end(knownPairs))
     464             :             {
     465           3 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     466             :                         "Field '%s' referenced in field definition of 0000 "
     467             :                         "control field is not a parent of a registered "
     468             :                         "(parent,child) pair.",
     469             :                         osParent.c_str())))
     470             :                 {
     471           1 :                     bRet = false;
     472             :                 }
     473             :             }
     474             :             else
     475             :             {
     476        3676 :                 if (!IsKnownChild(oIter->apszChildren, osChild) &&
     477           3 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     478             :                         "Field '%s' referenced in field definition of 0000 "
     479             :                         "control field is not an allowed child of a registered "
     480             :                         "('%s',child) pair.",
     481             :                         osChild.c_str(), osParent.c_str())))
     482             :                 {
     483           1 :                     bRet = false;
     484             :                 }
     485             :             }
     486             :         }
     487             :     }
     488             : 
     489         348 :     if (nPairs > 0)
     490             :     {
     491        5455 :         for (const std::string &osFieldName : oSetUsedFieldNames)
     492             :         {
     493        5109 :             if (GetParentIter(osFieldName) != std::end(knownPairs) &&
     494        5113 :                 !cpl::contains(oSetReferencedParent, osFieldName) &&
     495           4 :                 !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     496             :                     "Field '%s' is not referenced as a parent in field "
     497             :                     "definition of 0000 control field.",
     498             :                     osFieldName.c_str())))
     499             :             {
     500           2 :                 bRet = false;
     501             :             }
     502             : 
     503        5109 :             if (std::find_if(std::begin(knownPairs), std::end(knownPairs),
     504       31372 :                              [&IsKnownChild, &osFieldName](const auto &item)
     505             :                              {
     506       62744 :                                  return IsKnownChild(item.apszChildren,
     507       62744 :                                                      osFieldName);
     508        5109 :                              }) != std::end(knownPairs) &&
     509        5117 :                 !cpl::contains(oSetReferencedChildren, osFieldName) &&
     510           8 :                 !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     511             :                     "Field '%s' is not referenced as a child in field "
     512             :                     "definition of 0000 control field.",
     513             :                     osFieldName.c_str())))
     514             :             {
     515           4 :                 bRet = false;
     516             :             }
     517             :         }
     518             :     }
     519             : 
     520         348 :     return bRet;
     521             : }
     522             : 
     523             : /************************************************************************/
     524             : /*                              ReadDSSI()                              */
     525             : /************************************************************************/
     526             : 
     527             : /** Read the Dataset Structure Information (DSSI) field.
     528             :  */
     529         334 : bool OGRS101Reader::ReadDSSI(const DDFRecord *poRecord)
     530             : {
     531         334 :     const auto poField = poRecord->FindField(DSSI_FIELD);
     532         334 :     if (!poField)
     533           2 :         return EMIT_ERROR("DSSI field not found");
     534             : 
     535         332 :     int bSuccess = false;
     536             : 
     537             :     // must NOT be set as static, as it depens on "this" !
     538             :     const struct
     539             :     {
     540             :         const char *pszKey;
     541             :         double *pdfVal;
     542         332 :     } doubleFields[] = {
     543         332 :         {"DCOX", &m_dfXShift},
     544         332 :         {"DCOY", &m_dfYShift},
     545         332 :         {"DCOZ", &m_dfZShift},
     546         332 :     };
     547             : 
     548        1316 :     for (const auto &field : doubleFields)
     549             :     {
     550        1980 :         *(field.pdfVal) = poRecord->GetFloatSubfield(
     551         990 :             DSSI_FIELD, 0, field.pszKey, 0, &bSuccess);
     552         990 :         if (!bSuccess && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     553             :                              "no %s subfield of DSSI.", field.pszKey)))
     554             :         {
     555           0 :             return false;
     556             :         }
     557         990 :         if (std::isnan(*(field.pdfVal)))
     558             :         {
     559           6 :             return EMIT_ERROR(
     560             :                 CPLSPrintf("NaN value in %s subfield of DSSI.", field.pszKey));
     561             :         }
     562             :     }
     563             : 
     564         328 :     if (m_dfXShift != S101_SHIFT &&
     565           2 :         !EMIT_ERROR_OR_WARNING(
     566             :             "Value of DCOX subfield of DSSI is not at official value."))
     567             :     {
     568           1 :         return false;
     569             :     }
     570             : 
     571         327 :     if (m_dfYShift != S101_SHIFT &&
     572           2 :         !EMIT_ERROR_OR_WARNING(
     573             :             "Value of DCOY subfield of DSSI is not at official value."))
     574             :     {
     575           1 :         return false;
     576             :     }
     577             : 
     578         326 :     if (m_dfZShift != S101_SHIFT &&
     579           2 :         !EMIT_ERROR_OR_WARNING(
     580             :             "Value of DCOZ subfield of DSSI is not at official value."))
     581             :     {
     582           1 :         return false;
     583             :     }
     584             : 
     585             :     // must NOT be set as static, as it depens on "this" !
     586             :     const struct
     587             :     {
     588             :         const char *pszKey;
     589             :         int *pnVal;
     590         323 :     } intFields[] = {
     591         323 :         {"CMFX", &m_nXScale},
     592         323 :         {"CMFY", &m_nYScale},
     593         323 :         {"CMFZ", &m_nZScale},
     594         323 :         {"NOIR", &m_nCountInformationRecord},
     595         323 :         {"NOPN", &m_nCountPointRecord},
     596         323 :         {"NOMN", &m_nCountMultiPointRecord},
     597         323 :         {"NOCN", &m_nCountCurveRecord},
     598         323 :         {"NOXN", &m_nCountCompositeCurveRecord},
     599         323 :         {"NOSN", &m_nCountSurfaceRecord},
     600         323 :         {"NOFR", &m_nCountFeatureTypeRecord},
     601         323 :     };
     602             : 
     603        3525 :     for (const auto &field : intFields)
     604             :     {
     605        6418 :         *(field.pnVal) =
     606        3209 :             poRecord->GetIntSubfield(poField, field.pszKey, 0, &bSuccess);
     607        3209 :         if (!bSuccess && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     608             :                              "no %s subfield of DSSI", field.pszKey)))
     609             :         {
     610           0 :             return false;
     611             :         }
     612        3223 :         if (*(field.pnVal) < 0 &&
     613          14 :             !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     614             :                 "Invalid value for %s subfield of DSSI.", field.pszKey)))
     615             :         {
     616           7 :             return false;
     617             :         }
     618             :     }
     619             : 
     620         316 :     if (m_nXScale <= 0 || m_nYScale <= 0 || m_nZScale <= 0)
     621             :     {
     622           6 :         return EMIT_ERROR(
     623             :             "Invalid CMFX/CMFY/CMFZ scale factor in DSSI (must be > 0).");
     624             :     }
     625             : 
     626         312 :     if (m_nXScale != S101_XSCALE &&
     627           2 :         !EMIT_ERROR_OR_WARNING(
     628             :             "Value of CMFX subfield of DSSI is not at official value."))
     629             :     {
     630           1 :         return false;
     631             :     }
     632             : 
     633         311 :     if (m_nYScale != S101_YSCALE &&
     634           2 :         !EMIT_ERROR_OR_WARNING(
     635             :             "Value of CMFY subfield of DSSI is not at official value."))
     636             :     {
     637           1 :         return false;
     638             :     }
     639             : 
     640         310 :     if (m_nZScale != S101_ZSCALE &&
     641           2 :         !EMIT_ERROR_OR_WARNING(
     642             :             CPLSPrintf("Value of CMFZ subfield of DSSI is not at official "
     643             :                        "value. Got %d, expected %d.",
     644             :                        m_nZScale, S101_ZSCALE)))
     645             :     {
     646           1 :         return false;
     647             :     }
     648             : 
     649         307 :     return true;
     650             : }
     651             : 
     652             : /************************************************************************/
     653             : /*                     ReadGenericCodeAssociation()                     */
     654             : /************************************************************************/
     655             : 
     656             : /** Read fields like ATCS, ITCS, etc. that associate a numeric code to a
     657             :  * string
     658             :  */
     659             : template <class CodeType>
     660        1842 : bool OGRS101Reader::ReadGenericCodeAssociation(
     661             :     const DDFRecord *poRecord, const char *pszFieldName,
     662             :     const char *pszSubField0Name, const char *pszSubField1Name,
     663             :     std::map<CodeType, std::string> &map) const
     664             : {
     665        1842 :     const auto poField = poRecord->FindField(pszFieldName);
     666        1842 :     if (!poField)
     667             :     {
     668        1090 :         CPLDebugOnly("S101", "No %s field found", pszFieldName);
     669        1090 :         return true;
     670             :     }
     671             : 
     672         752 :     const int nRepeatCount = poField->GetRepeatCount();
     673        3900 :     for (int i = 0; i < nRepeatCount; ++i)
     674             :     {
     675        3148 :         int bSuccess = false;
     676        3148 :         const char *pszVal = poRecord->GetStringSubfield(
     677             :             poField, pszSubField0Name, i, &bSuccess);
     678        3148 :         if (!bSuccess)
     679             :         {
     680           0 :             if (!m_bStrict)
     681           0 :                 continue;
     682           0 :             return false;
     683             :         }
     684             :         const int nCode =
     685        3148 :             poRecord->GetIntSubfield(poField, pszSubField1Name, i, &bSuccess);
     686        3148 :         if (!bSuccess)
     687             :         {
     688           0 :             if (!m_bStrict)
     689           0 :                 continue;
     690           0 :             return false;
     691             :         }
     692        3148 :         if (!pszVal)
     693           0 :             pszVal = "(invalid)";
     694        3148 :         if (!map.insert({CodeType(nCode), pszVal}).second &&
     695           0 :             !EMIT_ERROR_OR_WARNING(
     696             :                 CPLSPrintf("%s: several definitions for %s %d.", pszFieldName,
     697             :                            pszSubField1Name, nCode)))
     698             :         {
     699           0 :             return false;
     700             :         }
     701             :     }
     702             : 
     703         752 :     return true;
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                              ReadATCS()                              */
     708             : /************************************************************************/
     709             : 
     710             : /** Read optional Attribute Codes field
     711             :  */
     712         307 : bool OGRS101Reader::ReadATCS(const DDFRecord *poRecord)
     713             : {
     714         614 :     return ReadGenericCodeAssociation<AttrCode>(poRecord, ATCS_FIELD, "ATCD",
     715         307 :                                                 "ANCD", m_attributeCodes);
     716             : }
     717             : 
     718             : /************************************************************************/
     719             : /*                              ReadITCS()                              */
     720             : /************************************************************************/
     721             : 
     722             : /** Read optional Feature Type Codes field
     723             :  */
     724         307 : bool OGRS101Reader::ReadITCS(const DDFRecord *poRecord)
     725             : {
     726         614 :     return ReadGenericCodeAssociation<InfoTypeCode>(
     727         307 :         poRecord, ITCS_FIELD, "ITCD", "ITNC", m_informationTypeCodes);
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                              ReadFTCS()                              */
     732             : /************************************************************************/
     733             : 
     734             : /** Read optional Feature Type Codes field
     735             :  */
     736         307 : bool OGRS101Reader::ReadFTCS(const DDFRecord *poRecord)
     737             : {
     738         614 :     return ReadGenericCodeAssociation<FeatureTypeCode>(
     739         307 :         poRecord, FTCS_FIELD, "FTCD", "FTNC", m_featureTypeCodes);
     740             : }
     741             : 
     742             : /************************************************************************/
     743             : /*                              ReadIACS()                              */
     744             : /************************************************************************/
     745             : 
     746             : /** Read optional Information Association Codes field
     747             :  */
     748         307 : bool OGRS101Reader::ReadIACS(const DDFRecord *poRecord)
     749             : {
     750         614 :     return ReadGenericCodeAssociation<InfoAssocCode>(
     751         307 :         poRecord, IACS_FIELD, "IACD", "IANC", m_informationAssociationCodes);
     752             : }
     753             : 
     754             : /************************************************************************/
     755             : /*                              ReadFACS()                              */
     756             : /************************************************************************/
     757             : 
     758             : /** Read optional Feature Association Codes field
     759             :  */
     760         307 : bool OGRS101Reader::ReadFACS(const DDFRecord *poRecord)
     761             : {
     762         614 :     return ReadGenericCodeAssociation<FeatureAssocCode>(
     763         307 :         poRecord, FACS_FIELD, "FACD", "FANC", m_featureAssociationCodes);
     764             : }
     765             : 
     766             : /************************************************************************/
     767             : /*                              ReadARCS()                              */
     768             : /************************************************************************/
     769             : 
     770             : /** Read optional Association Role Codes field
     771             :  */
     772         307 : bool OGRS101Reader::ReadARCS(const DDFRecord *poRecord)
     773             : {
     774         614 :     return ReadGenericCodeAssociation<AssocRoleCode>(
     775         307 :         poRecord, ARCS_FIELD, "ARCD", "ARNC", m_associationRoleCodes);
     776             : }

Generated by: LCOV version 1.14