LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101readerfeaturetype.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 553 585 94.5 %
Date: 2026-05-29 23:25:07 Functions: 16 16 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 "ogrs101featurecatalog.h"
      15             : #include "ogrs101readerconstants.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <memory>
      19             : 
      20             : /************************************************************************/
      21             : /*                   CreateFeatureTypeFeatureDefns()                    */
      22             : /************************************************************************/
      23             : 
      24             : /** Create the feature definitions for the various kinds of Feature Type records
      25             :  */
      26         342 : bool OGRS101Reader::CreateFeatureTypeFeatureDefns()
      27             : {
      28         342 :     const int nCount = m_oFeatureTypeRecordIndex.GetCount();
      29             : 
      30             :     struct FeatureTypeDef
      31             :     {
      32             :         int nMaxMaskCount = 0;
      33             :         bool bMultiSpatialAssociations = false;
      34             :         bool bPromotedToMultiPointFromPoint = false;
      35             :         std::vector<int> anRecordIdx{};
      36             :     };
      37             : 
      38         684 :     std::map<FeatureTypeKey, FeatureTypeDef> oMapFeatureClassAndGeomType;
      39             : 
      40             :     // First pass to collect all (feature type code, geometry type, CRS Id) triples
      41         762 :     for (int iRecord = 0; iRecord < nCount; ++iRecord)
      42             :     {
      43         422 :         const auto poRecord = m_oFeatureTypeRecordIndex.GetByIndex(iRecord);
      44         422 :         FeatureTypeKey key;
      45         422 :         key.nFeatureTypeCode =
      46             :             poRecord->GetIntSubfield(FRID_FIELD, 0, NFTC_SUBFIELD, 0);
      47             : 
      48         927 :         const auto NormalizeCurve = [](RecordName name)
      49             :         {
      50         927 :             return name == RECORD_NAME_COMPOSITE_CURVE ? RECORD_NAME_CURVE
      51         927 :                                                        : name;
      52             :         };
      53             : 
      54         422 :         bool bMultiSpatialAssociations = false;
      55             :         // S-101 page 19: A feature may reference multiple geometries
      56             :         // but must only reference geometries of a single
      57             :         // geometric primitive (point, pointset, curve or surface).
      58         422 :         const auto apoSPASFields = poRecord->GetFields(SPAS_FIELD);
      59         422 :         int nSPASCount = 0;
      60         422 :         bool bHeterogeneous = false;
      61         830 :         for (int iSPASField = 0;
      62         830 :              iSPASField < static_cast<int>(apoSPASFields.size()); ++iSPASField)
      63             :         {
      64         408 :             const auto poSPASField = apoSPASFields[iSPASField];
      65         408 :             if (iSPASField == 0)
      66             :             {
      67             :                 key.nGeometryType = NormalizeCurve(
      68         375 :                     poRecord->GetIntSubfield(poSPASField, RRNM_SUBFIELD, 0));
      69             :             }
      70             : 
      71         408 :             const int nSPASCountThisIter = poSPASField->GetRepeatCount();
      72         408 :             nSPASCount += nSPASCountThisIter;
      73         958 :             for (int iSPAS = 0; iSPAS < nSPASCountThisIter; ++iSPAS)
      74             :             {
      75         552 :                 if (NormalizeCurve(poRecord->GetIntSubfield(
      76         552 :                         poSPASField, RRNM_SUBFIELD, iSPAS)) !=
      77             :                     key.nGeometryType)
      78             :                 {
      79           2 :                     bHeterogeneous = true;
      80           2 :                     break;
      81             :                 }
      82             :             }
      83             :         }
      84         422 :         if (bHeterogeneous)
      85             :         {
      86           2 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
      87             :                     "Record index %d of FRID: has %d "
      88             :                     "spatial associations with at least 2 "
      89             :                     "not being of the same geometry type%s",
      90             :                     iRecord, nSPASCount, m_bStrict ? "" : ". Ignoring it")))
      91             :             {
      92           1 :                 return false;
      93             :             }
      94           1 :             continue;
      95             :         }
      96         420 :         else if (nSPASCount > 1)
      97             :         {
      98         173 :             bMultiSpatialAssociations = true;
      99             :         }
     100             : 
     101         420 :         if (key.nGeometryType == RECORD_NAME_POINT)
     102             :         {
     103             :             const int nRRID =
     104          98 :                 poRecord->GetIntSubfield(SPAS_FIELD, 0, RRID_SUBFIELD, 0);
     105          98 :             const auto poGeomRecord = m_oPointRecordIndex.FindRecord(nRRID);
     106          98 :             if (!poGeomRecord && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     107             :                                      "Record index %d of FRID: Point of id %d "
     108             :                                      "does not exist",
     109             :                                      iRecord, nRRID)))
     110             :             {
     111           1 :                 return false;
     112             :             }
     113             : 
     114             :             const auto nCRSId =
     115          96 :                 poGeomRecord ? GetCRSIdForPointRecord(poGeomRecord, -1, nRRID)
     116          97 :                              : HORIZONTAL_CRS_ID;
     117          97 :             if (nCRSId == INVALID_CRS_ID)
     118             :             {
     119             :                 // Error already emitted
     120           0 :                 if (m_bStrict)
     121           0 :                     return false;
     122           0 :                 continue;
     123             :             }
     124          97 :             key.nCRSId = nCRSId;
     125             :         }
     126         322 :         else if (key.nGeometryType == RECORD_NAME_MULTIPOINT)
     127             :         {
     128             :             const int nRRID =
     129          66 :                 poRecord->GetIntSubfield(SPAS_FIELD, 0, RRID_SUBFIELD, 0);
     130             :             const auto poGeomRecord =
     131          66 :                 m_oMultiPointRecordIndex.FindRecord(nRRID);
     132          66 :             if (!poGeomRecord &&
     133           0 :                 !EMIT_ERROR_OR_WARNING(
     134             :                     CPLSPrintf("Record index %d of FRID: MultiPoint of id %d "
     135             :                                "does not exist",
     136             :                                iRecord, nRRID)))
     137             :             {
     138           0 :                 return false;
     139             :             }
     140             : 
     141             :             const auto nCRSId =
     142             :                 poGeomRecord
     143          66 :                     ? GetCRSIdForMultiPointRecord(poGeomRecord, -1, nRRID)
     144          66 :                     : HORIZONTAL_CRS_ID;
     145          66 :             if (nCRSId == INVALID_CRS_ID)
     146             :             {
     147             :                 // Error already emitted
     148           0 :                 if (m_bStrict)
     149           0 :                     return false;
     150           0 :                 continue;
     151             :             }
     152          66 :             key.nCRSId = nCRSId;
     153             :         }
     154         256 :         else if (!apoSPASFields.empty())
     155         209 :             key.nCRSId = HORIZONTAL_CRS_ID;
     156             : 
     157             :         const bool bPromotedToMultiPointFromPoint =
     158         419 :             (key.nGeometryType == RECORD_NAME_POINT &&
     159         419 :              bMultiSpatialAssociations);
     160         419 :         if (bPromotedToMultiPointFromPoint)
     161          65 :             key.nGeometryType = RECORD_NAME_MULTIPOINT;
     162             : 
     163         419 :         auto &featureTypeDef = oMapFeatureClassAndGeomType[key];
     164         419 :         if (bPromotedToMultiPointFromPoint)
     165             :         {
     166          65 :             featureTypeDef.bPromotedToMultiPointFromPoint = true;
     167             :         }
     168         354 :         else if (bMultiSpatialAssociations)
     169             :         {
     170         108 :             featureTypeDef.bMultiSpatialAssociations = true;
     171             :         }
     172             : 
     173         419 :         int nMaskCount = 0;
     174         495 :         for (const auto poField : poRecord->GetFields(MASK_FIELD))
     175             :         {
     176          76 :             nMaskCount += poField->GetRepeatCount();
     177             :         }
     178         419 :         featureTypeDef.nMaxMaskCount =
     179         419 :             std::max(featureTypeDef.nMaxMaskCount, nMaskCount);
     180             : 
     181         419 :         featureTypeDef.anRecordIdx.push_back(iRecord);
     182             :     }
     183             : 
     184         640 :     for (auto &[key, def] : oMapFeatureClassAndGeomType)
     185             :     {
     186         302 :         std::string osLayerCode;
     187         302 :         std::string osName;
     188         302 :         const auto oIter = m_featureTypeCodes.find(key.nFeatureTypeCode);
     189         302 :         if (oIter != m_featureTypeCodes.end())
     190             :         {
     191         300 :             osLayerCode = oIter->second;
     192         300 :             osName = osLayerCode;
     193             :         }
     194             :         else
     195             :         {
     196           2 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     197             :                     "Features pointing at unknown feature type code %d",
     198             :                     static_cast<int>(key.nFeatureTypeCode))))
     199             :             {
     200           1 :                 return false;
     201             :             }
     202             :             osName = CPLSPrintf("unknownFeatureType%d",
     203           1 :                                 static_cast<int>(key.nFeatureTypeCode));
     204             :         }
     205             : 
     206         301 :         const auto oIterSRS = m_oMapSRS.find(key.nCRSId);
     207         301 :         CPLAssert(key.nCRSId == INVALID_CRS_ID || oIterSRS != m_oMapSRS.end());
     208             :         const OGRSpatialReference *poSRS =
     209         301 :             key.nCRSId != INVALID_CRS_ID ? &(oIterSRS->second) : nullptr;
     210         301 :         const bool bIs2D = key.nCRSId == HORIZONTAL_CRS_ID;
     211             : 
     212         301 :         OGRwkbGeometryType eGeomType = wkbNone;
     213         301 :         const char *pszExpectedPermittedPrimitive = nullptr;
     214         301 :         switch (static_cast<int>(key.nGeometryType))
     215             :         {
     216          44 :             case static_cast<int>(PSEUDO_RECORD_NAME_NO_GEOM):
     217             :             {
     218          44 :                 osName += "_NoGeom";
     219          44 :                 pszExpectedPermittedPrimitive =
     220             :                     OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_NO_GEOMETRY;
     221          44 :                 break;
     222             :             }
     223             : 
     224          30 :             case static_cast<int>(RECORD_NAME_POINT):
     225             :             {
     226          30 :                 CPLAssert(poSRS);
     227          30 :                 osName += '_';
     228          30 :                 if (def.bMultiSpatialAssociations)
     229             :                 {
     230           0 :                     osName += GetMultiPointLayerName(*poSRS);
     231           0 :                     eGeomType = bIs2D ? wkbMultiPoint : wkbMultiPoint25D;
     232             :                 }
     233             :                 else
     234             :                 {
     235          30 :                     osName += GetPointLayerName(*poSRS);
     236          30 :                     eGeomType = bIs2D ? wkbPoint : wkbPoint25D;
     237             :                 }
     238          30 :                 pszExpectedPermittedPrimitive =
     239             :                     OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_POINT;
     240          30 :                 break;
     241             :             }
     242             : 
     243          92 :             case static_cast<int>(RECORD_NAME_MULTIPOINT):
     244             :             {
     245          92 :                 CPLAssert(poSRS);
     246          92 :                 osName += '_';
     247          92 :                 if (def.bMultiSpatialAssociations)
     248             :                 {
     249          31 :                     osName += "CollectionOfMultiPoint";
     250          31 :                     eGeomType = wkbGeometryCollection;
     251             :                 }
     252             :                 else
     253             :                 {
     254          61 :                     osName += GetMultiPointLayerName(*poSRS);
     255          61 :                     eGeomType = bIs2D ? wkbMultiPoint : wkbMultiPoint25D;
     256             :                 }
     257          92 :                 pszExpectedPermittedPrimitive =
     258             :                     OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_POINTSET;
     259          92 :                 break;
     260             :             }
     261             : 
     262          62 :             case static_cast<int>(RECORD_NAME_CURVE):
     263             :             {
     264          62 :                 if (def.bMultiSpatialAssociations)
     265             :                 {
     266          31 :                     osName += "_MultiLine";
     267          31 :                     eGeomType = wkbMultiLineString;
     268             :                 }
     269             :                 else
     270             :                 {
     271          31 :                     osName += "_Line";
     272          31 :                     eGeomType = wkbLineString;
     273             :                 }
     274          62 :                 pszExpectedPermittedPrimitive =
     275             :                     OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_CURVE;
     276          62 :                 break;
     277             :             }
     278             : 
     279          71 :             case static_cast<int>(RECORD_NAME_SURFACE):
     280             :             {
     281          71 :                 if (def.bMultiSpatialAssociations)
     282             :                 {
     283          40 :                     osName += "_MultiPolygon";
     284          40 :                     eGeomType = wkbMultiPolygon;
     285             :                 }
     286             :                 else
     287             :                 {
     288          31 :                     osName += "_Polygon";
     289          31 :                     eGeomType = wkbPolygon;
     290             :                 }
     291          71 :                 pszExpectedPermittedPrimitive =
     292             :                     OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_SURFACE;
     293          71 :                 break;
     294             :             }
     295             : 
     296           2 :             default:
     297             :             {
     298           2 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     299             :                         "Features pointing at unknown spatial record type %d",
     300             :                         static_cast<int>(key.nGeometryType))))
     301             :                 {
     302           1 :                     return false;
     303             :                 }
     304           1 :                 osName += "_UnknownGeomType";
     305           1 :                 osName += std::to_string(static_cast<int>(key.nGeometryType));
     306           1 :                 break;
     307             :             }
     308             :         }
     309             : 
     310         300 :         const OGRS101FeatureCatalog::FeatureType *psFeatureType = nullptr;
     311         300 :         if (m_poFeatureCatalog)
     312             :         {
     313             :             const auto oIterFT =
     314         300 :                 m_poFeatureCatalog->GetFeatureTypes().find(osLayerCode);
     315         300 :             if (oIterFT != m_poFeatureCatalog->GetFeatureTypes().end())
     316             :             {
     317           0 :                 psFeatureType = &(oIterFT->second);
     318             :             }
     319         301 :             else if (!cpl::starts_with(osLayerCode, "FeatureType") &&
     320           1 :                      !EMIT_ERROR_OR_WARNING(
     321             :                          CPLSPrintf("Feature type %s is not referenced in the "
     322             :                                     "feature catalog",
     323             :                                     osLayerCode.c_str())))
     324             :             {
     325           0 :                 return false;
     326             :             }
     327             :         }
     328             : 
     329         299 :         if (pszExpectedPermittedPrimitive && psFeatureType &&
     330           0 :             !cpl::contains(psFeatureType->permittedPrimitives,
     331         599 :                            pszExpectedPermittedPrimitive) &&
     332           0 :             !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     333             :                 "Features of %s contain records with primitive %s, whereas "
     334             :                 "it is not allowed by the feature catalog",
     335             :                 osLayerCode.c_str(), pszExpectedPermittedPrimitive)))
     336             :         {
     337           0 :             return false;
     338             :         }
     339             : 
     340             :         auto poFDefn =
     341         300 :             OGRFeatureDefnRefCountedPtr::makeInstance(osName.c_str());
     342         300 :         poFDefn->SetGeomType(eGeomType);
     343        1500 :         for (const char *pszOGRFieldName :
     344             :              {OGR_FIELD_NAME_RECORD_ID, OGR_FIELD_NAME_RECORD_VERSION,
     345        1800 :               OGR_FIELD_NAME_AGEN, OGR_FIELD_NAME_FIDN, OGR_FIELD_NAME_FIDS})
     346             :         {
     347        3000 :             OGRFieldDefn oFieldDefn(pszOGRFieldName, OFTInteger);
     348        1500 :             poFDefn->AddFieldDefn(&oFieldDefn);
     349             :         }
     350             : 
     351         300 :         if (eGeomType != wkbNone)
     352             :         {
     353         255 :             CPLAssert(poSRS);
     354         510 :             poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(
     355         510 :                 OGRSpatialReferenceRefCountedPtr::makeClone(poSRS).get());
     356         255 :             poFDefn->GetGeomFieldDefn(0)->SetCoordinatePrecision(
     357         255 :                 m_coordinatePrecision);
     358         408 :             const bool bList = def.bMultiSpatialAssociations ||
     359         153 :                                def.bPromotedToMultiPointFromPoint;
     360             : 
     361             :             {
     362             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_GEOMETRY_LAYER_NAME,
     363         510 :                                         bList ? OFTStringList : OFTString);
     364         255 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     365             :             }
     366         255 :             for (const char *pszOGRFieldName :
     367         510 :                  {OGR_FIELD_NAME_GEOMETRY_RECORD_ID})
     368             :             {
     369             :                 OGRFieldDefn oFieldDefn(pszOGRFieldName,
     370         510 :                                         bList ? OFTIntegerList : OFTInteger);
     371         255 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     372             :             }
     373         255 :             if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
     374             :             {
     375             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_GEOMETRY_ORIENTATION,
     376         124 :                                         bList ? OFTStringList : OFTString);
     377          62 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     378             :             }
     379         510 :             for (const char *pszOGRFieldName :
     380         765 :                  {OGR_FIELD_NAME_SMIN, OGR_FIELD_NAME_SMAX})
     381             :             {
     382             :                 OGRFieldDefn oFieldDefn(pszOGRFieldName,
     383        1020 :                                         bList ? OFTIntegerList : OFTInteger);
     384         510 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     385             :             }
     386             :         }
     387             : 
     388         900 :         for (const char *pszAttrFieldName :
     389        1200 :              {ATTR_FIELD, INAS_FIELD, FASC_FIELD})
     390             :         {
     391        1800 :             if (!InferFeatureDefn(
     392         900 :                     m_oFeatureTypeRecordIndex, FRID_FIELD, pszAttrFieldName,
     393         900 :                     def.anRecordIdx, *poFDefn, m_oMapFieldDomains, nullptr,
     394         900 :                     strcmp(pszAttrFieldName, ATTR_FIELD) == 0 ? psFeatureType
     395             :                                                               : nullptr))
     396             :             {
     397           0 :                 return false;
     398             :             }
     399             :         }
     400             : 
     401         300 :         if (def.nMaxMaskCount == 1)
     402             :         {
     403             :             {
     404             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_LAYER_NAME,
     405          12 :                                         OFTString);
     406           6 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     407             :             }
     408             :             {
     409             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_RECORD_ID,
     410          12 :                                         OFTInteger);
     411           6 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     412             :             }
     413             :             {
     414             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_INDICATOR,
     415          12 :                                         OFTString);
     416           6 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     417             :             }
     418             : 
     419          12 :             OGRGeomFieldDefn oGeomFieldDefn("maskGeometry", wkbLineString);
     420           6 :             oGeomFieldDefn.SetSpatialRef(
     421           6 :                 OGRSpatialReferenceRefCountedPtr::makeClone(
     422           6 :                     &(m_oMapSRS[HORIZONTAL_CRS_ID]))
     423           6 :                     .get());
     424           6 :             poFDefn->AddGeomFieldDefn(&oGeomFieldDefn);
     425             :         }
     426         294 :         else if (def.nMaxMaskCount > 1)
     427             :         {
     428             :             {
     429             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_LAYER_NAME,
     430          68 :                                         OFTStringList);
     431          34 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     432             :             }
     433             :             {
     434             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_RECORD_ID,
     435          68 :                                         OFTIntegerList);
     436          34 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     437             :             }
     438             :             {
     439             :                 OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_INDICATOR,
     440          68 :                                         OFTStringList);
     441          34 :                 poFDefn->AddFieldDefn(&oFieldDefn);
     442             :             }
     443             : 
     444          68 :             OGRGeomFieldDefn oGeomFieldDefn("maskGeometry", wkbMultiLineString);
     445          34 :             oGeomFieldDefn.SetSpatialRef(
     446          34 :                 OGRSpatialReferenceRefCountedPtr::makeClone(
     447          34 :                     &(m_oMapSRS[HORIZONTAL_CRS_ID]))
     448          34 :                     .get());
     449          34 :             poFDefn->AddGeomFieldDefn(&oGeomFieldDefn);
     450             :         }
     451             : 
     452         693 :         for (int nRecordIdx : def.anRecordIdx)
     453             :         {
     454             :             const int nRCID =
     455             :                 m_oFeatureTypeRecordIndex.GetByIndex(nRecordIdx)
     456         393 :                     ->GetIntSubfield(FRID_FIELD, 0, RCID_SUBFIELD, 0);
     457         393 :             m_oMapFeatureTypeIdToFDefn[nRCID] = poFDefn.get();
     458             :         }
     459             : 
     460         600 :         LayerDef layerDef;
     461         300 :         layerDef.poFeatureDefn = std::move(poFDefn);
     462         300 :         if (psFeatureType)
     463             :         {
     464           0 :             layerDef.osName = psFeatureType->name;
     465           0 :             layerDef.osDefinition = psFeatureType->definition;
     466           0 :             layerDef.osAlias = psFeatureType->alias;
     467             :         }
     468         300 :         layerDef.anRecordIndices = std::move(def.anRecordIdx);
     469         300 :         m_oMapFeatureKeyToLayerDef[key] = std::move(layerDef);
     470             :     }
     471             : 
     472         338 :     return true;
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                            ReadGeometry()                            */
     477             : /************************************************************************/
     478             : 
     479             : template <typename T, typename GeomReaderMethodType>
     480        1313 : bool OGRS101Reader::ReadGeometry(
     481             :     const DDFRecordIndex &oIndex, const char *pszErrorContext,
     482             :     int nGeomRecordID, const char *pszGeomType, bool bReverse,
     483             :     OGRFeature &oFeature, std::unique_ptr<OGRGeometryCollection> &poMultiGeom,
     484             :     GeomReaderMethodType geomReaderMethod, int iGeomField) const
     485             : {
     486        1313 :     const auto poGeomRecord = oIndex.FindRecord(nGeomRecordID);
     487             : 
     488        1313 :     if (!poGeomRecord && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     489             :                              "%s: %s of ID=%d does not exist", pszErrorContext,
     490             :                              pszGeomType, nGeomRecordID)))
     491             :     {
     492           3 :         return false;
     493             :     }
     494             : 
     495             :     const auto poGeomFieldDefn =
     496        1310 :         oFeature.GetDefnRef()->GetGeomFieldDefn(iGeomField);
     497        1310 :     const OGRSpatialReference *poSRS = poGeomFieldDefn->GetSpatialRef();
     498        1310 :     std::unique_ptr<T> poGeom;
     499        1310 :     if (poGeomRecord)
     500             :     {
     501        1303 :         poGeom = (this->*geomReaderMethod)(poGeomRecord, /* index = */ -1,
     502             :                                            nGeomRecordID, poSRS);
     503             :     }
     504        1310 :     if (!poGeom)
     505             :     {
     506           7 :         if (poGeomRecord && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     507             :                                 "%s: %s of ID=%d is invalid", pszErrorContext,
     508             :                                 pszGeomType, nGeomRecordID)))
     509             :         {
     510           0 :             return false;
     511             :         }
     512             :     }
     513             :     else if constexpr (std::is_same_v<T, OGRLineString>)
     514             :     {
     515         525 :         if (bReverse)
     516         103 :             poGeom->reversePoints();
     517             :     }
     518             : 
     519        1310 :     const auto eGeomType = poGeomFieldDefn->GetType();
     520             : 
     521        2456 :     if (wkbFlatten(eGeomType) == wkbGeometryCollection ||
     522             :         (!std::is_same_v<T, OGRMultiPoint> &&
     523        1146 :          OGR_GT_IsSubClassOf(eGeomType, wkbGeometryCollection)))
     524             :     {
     525         936 :         if (!poMultiGeom)
     526             :         {
     527         434 :             poMultiGeom = std::make_unique<typename T::MultiType>();
     528         434 :             poMultiGeom->assignSpatialReference(poSRS);
     529             :         }
     530         936 :         poMultiGeom->addGeometry(poGeom ? std::move(poGeom)
     531             :                                         : std::make_unique<T>());
     532             :     }
     533             :     else
     534             :     {
     535         374 :         oFeature.SetGeomField(iGeomField, std::move(poGeom));
     536             :     }
     537             : 
     538        1310 :     return true;
     539             : }
     540             : 
     541             : /************************************************************************/
     542             : /*                      FillFeatureTypeGeometry()                       */
     543             : /************************************************************************/
     544             : 
     545             : /** Fill the geometry of the provided feature from the identified record
     546             :  * (of m_oFeatureTypeRecordIndex).
     547             :  */
     548         789 : bool OGRS101Reader::FillFeatureTypeGeometry(const DDFRecord *poRecord,
     549             :                                             int iRecord,
     550             :                                             OGRFeature &oFeature) const
     551             : {
     552             :     // Process geometry (several spatial association per feature possible)
     553        1578 :     CPLStringList aosLayerNames, aosOrientations;
     554        1578 :     std::vector<int> anRRID, anSMIN, anSMAX;
     555         789 :     std::unique_ptr<OGRGeometryCollection> poMultiGeom;
     556             : 
     557        1578 :     const auto apoSPASFields = poRecord->GetFields(SPAS_FIELD);
     558        1532 :     for (const auto &poSPASField : apoSPASFields)
     559             :     {
     560         749 :         const int nSPASCount = poSPASField->GetRepeatCount();
     561             : 
     562        1796 :         for (int iSPAS = 0; iSPAS < nSPASCount; ++iSPAS)
     563             :         {
     564             :             const auto GetIntSubfield =
     565        6301 :                 [poRecord, poSPASField, iSPAS](const char *pszSubFieldName)
     566             :             {
     567        6301 :                 return poRecord->GetIntSubfield(poSPASField, pszSubFieldName,
     568        6301 :                                                 iSPAS);
     569        1053 :             };
     570             : 
     571             :             const std::string osErrorContext =
     572             :                 CPLSPrintf("Feature type record index %d, SPAS instance %d",
     573        1053 :                            iRecord, iSPAS);
     574             : 
     575        1053 :             const int nSAUI = GetIntSubfield(SAUI_SUBFIELD);
     576        1053 :             if (nSAUI != INSTRUCTION_INSERT)
     577             :             {
     578           3 :                 if (!EMIT_ERROR_OR_WARNING(
     579             :                         CPLSPrintf("%s: SAUI value %d is invalid",
     580             :                                    osErrorContext.c_str(), nSAUI)))
     581             :                 {
     582           1 :                     return false;
     583             :                 }
     584             :             }
     585             : 
     586        1052 :             const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
     587             : 
     588        1052 :             const int nRRID = GetIntSubfield(RRID_SUBFIELD);
     589             : 
     590        1052 :             const int nORNT = GetIntSubfield(ORNT_SUBFIELD);
     591             : 
     592        1883 :             const bool bIsLine = nRRNM == RECORD_NAME_CURVE ||
     593         831 :                                  nRRNM == RECORD_NAME_COMPOSITE_CURVE;
     594        1052 :             switch (nORNT)
     595             :             {
     596         164 :                 case ORNT_FORWARD:
     597             :                 {
     598         164 :                     break;
     599             :                 }
     600             : 
     601         106 :                 case ORNT_REVERSE:
     602             :                 {
     603         106 :                     if (!bIsLine)
     604             :                     {
     605           3 :                         if (!EMIT_ERROR_OR_WARNING(
     606             :                                 CPLSPrintf("%s: "
     607             :                                            "ORNT = Reverse is invalid for "
     608             :                                            "non-curve geometry",
     609             :                                            osErrorContext.c_str())))
     610             :                         {
     611           1 :                             return false;
     612             :                         }
     613             :                     }
     614         105 :                     break;
     615             :                 }
     616             : 
     617         779 :                 case ORNT_NULL:
     618             :                 {
     619         779 :                     if (bIsLine)
     620             :                     {
     621           0 :                         if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     622             :                                 "%s: ORNT = Null is invalid for curve geometry",
     623             :                                 osErrorContext.c_str())))
     624             :                         {
     625           0 :                             return false;
     626             :                         }
     627             :                     }
     628         779 :                     break;
     629             :                 }
     630             : 
     631           3 :                 default:
     632             :                 {
     633           3 :                     if (!EMIT_ERROR_OR_WARNING(
     634             :                             CPLSPrintf("%s: ORNT = %d is invalid",
     635             :                                        osErrorContext.c_str(), nORNT)))
     636             :                     {
     637           1 :                         return false;
     638             :                     }
     639           2 :                     break;
     640             :                 }
     641             :             }
     642             : 
     643        1050 :             bool bInvalidType = false;
     644        1050 :             switch (static_cast<int>(nRRNM))
     645             :             {
     646         302 :                 case static_cast<int>(RECORD_NAME_POINT):
     647             :                 {
     648         604 :                     if (!ReadGeometry<OGRPoint>(
     649         302 :                             m_oPointRecordIndex, osErrorContext.c_str(), nRRID,
     650             :                             "Point", false, oFeature, poMultiGeom,
     651             :                             &OGRS101Reader::ReadPointGeometry))
     652             :                     {
     653           0 :                         return false;
     654             :                     }
     655             : 
     656             :                     const auto poGeomFieldDefn =
     657         302 :                         oFeature.GetDefnRef()->GetGeomFieldDefn(0);
     658         302 :                     CPLAssert(poGeomFieldDefn);
     659             :                     const OGRSpatialReference *poSRS =
     660         302 :                         poGeomFieldDefn->GetSpatialRef();
     661         302 :                     CPLAssert(poSRS);
     662             : 
     663         302 :                     aosLayerNames.push_back(GetPointLayerName(*poSRS).c_str());
     664             : 
     665         302 :                     break;
     666             :                 }
     667             : 
     668         164 :                 case static_cast<int>(RECORD_NAME_MULTIPOINT):
     669             :                 {
     670         328 :                     if (!ReadGeometry<OGRMultiPoint>(
     671         164 :                             m_oMultiPointRecordIndex, osErrorContext.c_str(),
     672             :                             nRRID, "MultiPoint", false, oFeature, poMultiGeom,
     673             :                             &OGRS101Reader::ReadMultiPointGeometry))
     674             :                     {
     675           0 :                         return false;
     676             :                     }
     677             : 
     678             :                     const auto poGeomFieldDefn =
     679         164 :                         oFeature.GetDefnRef()->GetGeomFieldDefn(0);
     680         164 :                     CPLAssert(poGeomFieldDefn);
     681             :                     const OGRSpatialReference *poSRS =
     682         164 :                         poGeomFieldDefn->GetSpatialRef();
     683         164 :                     CPLAssert(poSRS);
     684             : 
     685         164 :                     aosLayerNames.push_back(
     686         328 :                         GetMultiPointLayerName(*poSRS).c_str());
     687             : 
     688         164 :                     break;
     689             :                 }
     690             : 
     691         267 :                 case static_cast<int>(RECORD_NAME_CURVE):
     692             :                 case static_cast<int>(RECORD_NAME_COMPOSITE_CURVE):
     693             :                 {
     694             :                     const char *pszLayerName =
     695         267 :                         nRRNM == RECORD_NAME_CURVE
     696             :                             ? OGR_LAYER_NAME_CURVE
     697         267 :                             : OGR_LAYER_NAME_COMPOSITE_CURVE;
     698             : 
     699             :                     bool ret;
     700         267 :                     if (nRRNM == RECORD_NAME_CURVE)
     701             :                     {
     702         442 :                         ret = ReadGeometry<OGRLineString>(
     703         221 :                             m_oCurveRecordIndex, osErrorContext.c_str(), nRRID,
     704             :                             pszLayerName, nORNT == ORNT_REVERSE, oFeature,
     705             :                             poMultiGeom, &OGRS101Reader::ReadCurveGeometry);
     706             :                     }
     707             :                     else
     708             :                     {
     709          92 :                         ret = ReadGeometry<OGRLineString>(
     710          46 :                             m_oCompositeCurveRecordIndex,
     711             :                             osErrorContext.c_str(), nRRID, pszLayerName,
     712             :                             nORNT == ORNT_REVERSE, oFeature, poMultiGeom,
     713             :                             &OGRS101Reader::ReadCompositeCurveGeometry);
     714             :                     }
     715             : 
     716         267 :                     if (!ret)
     717             :                     {
     718           2 :                         return false;
     719             :                     }
     720             : 
     721         265 :                     aosLayerNames.push_back(pszLayerName);
     722         265 :                     aosOrientations.push_back(
     723             :                         nORNT == ORNT_FORWARD ? "forward" : "reverse");
     724             : 
     725         265 :                     break;
     726             :                 }
     727             : 
     728         316 :                 case static_cast<int>(RECORD_NAME_SURFACE):
     729             :                 {
     730         632 :                     if (!ReadGeometry<OGRPolygon>(
     731         316 :                             m_oSurfaceRecordIndex, osErrorContext.c_str(),
     732             :                             nRRID, OGR_LAYER_NAME_SURFACE, false, oFeature,
     733             :                             poMultiGeom, &OGRS101Reader::ReadSurfaceGeometry))
     734             :                     {
     735           1 :                         return false;
     736             :                     }
     737             : 
     738         315 :                     aosLayerNames.push_back(OGR_LAYER_NAME_SURFACE);
     739             : 
     740         315 :                     break;
     741             :                 }
     742             : 
     743           1 :                 default:
     744           1 :                     bInvalidType = true;
     745           1 :                     break;
     746             :             }
     747             : 
     748        1047 :             if (bInvalidType)
     749             :             {
     750           1 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: "
     751             :                                                       "Invalid RRNM = %d",
     752             :                                                       osErrorContext.c_str(),
     753             :                                                       static_cast<int>(nRRNM))))
     754             :                 {
     755           0 :                     return false;
     756             :                 }
     757             :             }
     758             :             else
     759             :             {
     760        1046 :                 anRRID.push_back(nRRID);
     761             : 
     762        1046 :                 anSMIN.push_back(GetIntSubfield(SMIN_SUBFIELD));
     763             : 
     764        1046 :                 anSMAX.push_back(GetIntSubfield(SMAX_SUBFIELD));
     765             :             }
     766             :         }
     767             :     }
     768             : 
     769         783 :     if (!apoSPASFields.empty())
     770             :     {
     771         683 :         CPLAssert(anRRID.size() == anSMIN.size());
     772         683 :         CPLAssert(anRRID.size() == anSMAX.size());
     773         683 :         CPLAssert(anRRID.size() == static_cast<size_t>(aosLayerNames.size()));
     774         683 :         CPLAssert(aosOrientations.empty() ||
     775             :                   anRRID.size() == static_cast<size_t>(aosOrientations.size()));
     776             : 
     777         683 :         if (poMultiGeom)
     778             :         {
     779         364 :             oFeature.SetGeometry(std::move(poMultiGeom));
     780             : 
     781         364 :             oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_LAYER_NAME,
     782         364 :                               aosLayerNames.List());
     783         364 :             if (!aosOrientations.empty())
     784             :             {
     785          57 :                 oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_ORIENTATION,
     786          57 :                                   aosOrientations.List());
     787             :             }
     788         364 :             oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_RECORD_ID,
     789         364 :                               static_cast<int>(anRRID.size()), anRRID.data());
     790         364 :             if (std::find_if(anSMIN.begin(), anSMIN.end(),
     791         847 :                              [](int x) { return x > 0; }) != anSMIN.end())
     792             :             {
     793         245 :                 oFeature.SetField(OGR_FIELD_NAME_SMIN,
     794         245 :                                   static_cast<int>(anSMIN.size()),
     795         245 :                                   anSMIN.data());
     796             :             }
     797         364 :             if (std::find_if(anSMAX.begin(), anSMAX.end(),
     798         847 :                              [](int x) { return x > 0; }) != anSMAX.end())
     799             :             {
     800         245 :                 oFeature.SetField(OGR_FIELD_NAME_SMAX,
     801         245 :                                   static_cast<int>(anSMAX.size()),
     802         245 :                                   anSMAX.data());
     803             :             }
     804             :         }
     805         319 :         else if (!aosLayerNames.empty())
     806             :         {
     807         318 :             oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_LAYER_NAME,
     808         318 :                               aosLayerNames[0]);
     809         318 :             oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_RECORD_ID, anRRID[0]);
     810         318 :             if (!aosOrientations.empty())
     811             :             {
     812         151 :                 oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_ORIENTATION,
     813         151 :                                   aosOrientations[0]);
     814             :             }
     815         318 :             if (anSMIN[0] > 0)
     816         254 :                 oFeature.SetField(OGR_FIELD_NAME_SMIN, anSMIN[0]);
     817         318 :             if (anSMAX[0] > 0)
     818         254 :                 oFeature.SetField(OGR_FIELD_NAME_SMAX, anSMAX[0]);
     819             :         }
     820             :     }
     821             : 
     822         783 :     return true;
     823             : }
     824             : 
     825             : /************************************************************************/
     826             : /*                        FillFeatureTypeMask()                         */
     827             : /************************************************************************/
     828             : 
     829             : /** Fill the mask info of the provided feature from the identified record
     830             :  * (of m_oFeatureTypeRecordIndex).
     831             :  */
     832         783 : bool OGRS101Reader::FillFeatureTypeMask(const DDFRecord *poRecord, int iRecord,
     833             :                                         OGRFeature &oFeature) const
     834             : {
     835         783 :     std::unique_ptr<OGRGeometryCollection> poMultiGeom;
     836             : 
     837        1566 :     std::vector<int> anRRID;
     838        1566 :     CPLStringList aosLayerNames;
     839        1566 :     CPLStringList aosMaskIndicators;
     840             : 
     841         783 :     if (oFeature.GetDefnRef()->GetGeomFieldCount() != 2)
     842         654 :         return true;
     843             : 
     844         129 :     constexpr int MASK_GEOM_FIELD_IDX = 1;
     845             : 
     846         258 :     const auto apoMASKFields = poRecord->GetFields(MASK_FIELD);
     847         325 :     for (const auto *poMASKField : apoMASKFields)
     848             :     {
     849         199 :         const int nMaskCount = poMASKField->GetRepeatCount();
     850             : 
     851         465 :         for (int iMASK = 0; iMASK < nMaskCount; ++iMASK)
     852             :         {
     853             :             const auto GetIntSubfield =
     854        1071 :                 [poRecord, poMASKField, iMASK](const char *pszSubFieldName)
     855             :             {
     856        1071 :                 return poRecord->GetIntSubfield(poMASKField, pszSubFieldName,
     857        1071 :                                                 iMASK);
     858         269 :             };
     859             : 
     860             :             const std::string osErrorContext =
     861             :                 CPLSPrintf("Feature type record index %d, MASK instance %d",
     862         269 :                            iRecord, iMASK);
     863             : 
     864         269 :             const int nMUIN = GetIntSubfield(MUIN_SUBFIELD);
     865         269 :             if (nMUIN != INSTRUCTION_INSERT)
     866             :             {
     867           3 :                 if (!EMIT_ERROR_OR_WARNING(
     868             :                         CPLSPrintf("%s: MUIN value %d is invalid",
     869             :                                    osErrorContext.c_str(), nMUIN)))
     870             :                 {
     871           1 :                     return false;
     872             :                 }
     873             :             }
     874             : 
     875         268 :             const int nMIND = GetIntSubfield(MIND_SUBFIELD);
     876         268 :             constexpr int MASK_INDICATOR_TRUNCATED_BY_DATA_COVERAGE_LIMIT = 1;
     877         268 :             constexpr int MASK_INDICATOR_SUPPRESS_PORTRAYAL = 2;
     878         268 :             if (nMIND == MASK_INDICATOR_TRUNCATED_BY_DATA_COVERAGE_LIMIT)
     879             :             {
     880         195 :                 aosMaskIndicators.push_back("truncatedByDataCoverageLimit");
     881             :             }
     882          73 :             else if (nMIND == MASK_INDICATOR_SUPPRESS_PORTRAYAL)
     883             :             {
     884          70 :                 aosMaskIndicators.push_back("suppressPortrayal");
     885             :             }
     886             :             else
     887             :             {
     888           3 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     889             :                         "%s: MIND value %d is invalid. Expected %d or %d",
     890             :                         osErrorContext.c_str(), nMIND,
     891             :                         MASK_INDICATOR_TRUNCATED_BY_DATA_COVERAGE_LIMIT,
     892             :                         MASK_INDICATOR_SUPPRESS_PORTRAYAL)))
     893             :                 {
     894           1 :                     return false;
     895             :                 }
     896           2 :                 aosMaskIndicators.push_back(CPLSPrintf("unknown%d", nMIND));
     897             :             }
     898             : 
     899         267 :             const int nRRID = GetIntSubfield(RRID_SUBFIELD);
     900         267 :             anRRID.push_back(nRRID);
     901             : 
     902         267 :             const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
     903         340 :             if (nRRNM == RECORD_NAME_CURVE ||
     904          73 :                 nRRNM == RECORD_NAME_COMPOSITE_CURVE)
     905             :             {
     906         264 :                 const char *pszLayerName = nRRNM == RECORD_NAME_CURVE
     907             :                                                ? OGR_LAYER_NAME_CURVE
     908         264 :                                                : OGR_LAYER_NAME_COMPOSITE_CURVE;
     909             : 
     910             :                 bool ret;
     911         264 :                 if (nRRNM == RECORD_NAME_CURVE)
     912             :                 {
     913         388 :                     ret = ReadGeometry<OGRLineString>(
     914         194 :                         m_oCurveRecordIndex, osErrorContext.c_str(), nRRID,
     915             :                         pszLayerName, false, oFeature, poMultiGeom,
     916             :                         &OGRS101Reader::ReadCurveGeometry, MASK_GEOM_FIELD_IDX);
     917             :                 }
     918             :                 else
     919             :                 {
     920         140 :                     ret = ReadGeometry<OGRLineString>(
     921          70 :                         m_oCompositeCurveRecordIndex, osErrorContext.c_str(),
     922             :                         nRRID, pszLayerName, false, oFeature, poMultiGeom,
     923             :                         &OGRS101Reader::ReadCompositeCurveGeometry,
     924             :                         MASK_GEOM_FIELD_IDX);
     925             :                 }
     926             : 
     927         264 :                 if (!ret)
     928             :                 {
     929           0 :                     return false;
     930             :                 }
     931             : 
     932         264 :                 aosLayerNames.push_back(pszLayerName);
     933             :             }
     934             :             else
     935             :             {
     936           3 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     937             :                         "%s: Invalid value for RRNM subfield: "
     938             :                         "got %d, expected %d or %d.",
     939             :                         osErrorContext.c_str(), static_cast<int>(nRRNM),
     940             :                         static_cast<int>(RECORD_NAME_CURVE),
     941             :                         static_cast<int>(RECORD_NAME_COMPOSITE_CURVE))))
     942             :                 {
     943           1 :                     return false;
     944             :                 }
     945             : 
     946           2 :                 aosLayerNames.push_back("");
     947             :             }
     948             :         }
     949             :     }
     950             : 
     951         126 :     if (!aosLayerNames.empty())
     952             :     {
     953         126 :         CPLAssert(anRRID.size() == static_cast<size_t>(aosLayerNames.size()));
     954         126 :         CPLAssert(anRRID.size() ==
     955             :                   static_cast<size_t>(aosMaskIndicators.size()));
     956             : 
     957         126 :         if (poMultiGeom)
     958             :         {
     959          70 :             oFeature.SetGeomField(MASK_GEOM_FIELD_IDX, std::move(poMultiGeom));
     960             : 
     961          70 :             oFeature.SetField(OGR_FIELD_NAME_MASK_LAYER_NAME,
     962          70 :                               aosLayerNames.List());
     963             : 
     964          70 :             oFeature.SetField(OGR_FIELD_NAME_MASK_RECORD_ID,
     965          70 :                               static_cast<int>(anRRID.size()), anRRID.data());
     966             : 
     967          70 :             oFeature.SetField(OGR_FIELD_NAME_MASK_INDICATOR,
     968          70 :                               aosMaskIndicators.List());
     969             :         }
     970             :         else
     971             :         {
     972          56 :             oFeature.SetField(OGR_FIELD_NAME_MASK_LAYER_NAME, aosLayerNames[0]);
     973             : 
     974          56 :             oFeature.SetField(OGR_FIELD_NAME_MASK_RECORD_ID, anRRID[0]);
     975             : 
     976          56 :             oFeature.SetField(OGR_FIELD_NAME_MASK_INDICATOR,
     977          56 :                               aosMaskIndicators[0]);
     978             :         }
     979             :     }
     980             : 
     981         126 :     return true;
     982             : }
     983             : 
     984             : /************************************************************************/
     985             : /*                       FillFeatureFeatureType()                       */
     986             : /************************************************************************/
     987             : 
     988             : /** Fill the content of the provided feature from the identified record
     989             :  * (of m_oFeatureTypeRecordIndex).
     990             :  */
     991         789 : bool OGRS101Reader::FillFeatureFeatureType(const DDFRecordIndex &oIndex,
     992             :                                            int iRecord,
     993             :                                            OGRFeature &oFeature) const
     994             : {
     995         789 :     const auto poRecord = oIndex.GetByIndex(iRecord);
     996         789 :     CPLAssert(poRecord);
     997             : 
     998         789 :     if (const auto poFOIDField = poRecord->FindField(FOID_FIELD))
     999             :     {
    1000             :         const int nAGEN =
    1001         788 :             poRecord->GetIntSubfield(poFOIDField, AGEN_SUBFIELD, 0);
    1002         788 :         oFeature.SetField(OGR_FIELD_NAME_AGEN, nAGEN);
    1003             : 
    1004             :         const int nFIDN =
    1005         788 :             poRecord->GetIntSubfield(poFOIDField, FIDN_SUBFIELD, 0);
    1006         788 :         oFeature.SetField(OGR_FIELD_NAME_FIDN, nFIDN);
    1007             : 
    1008             :         const int nFIDS =
    1009         788 :             poRecord->GetIntSubfield(poFOIDField, FIDS_SUBFIELD, 0);
    1010         788 :         oFeature.SetField(OGR_FIELD_NAME_FIDS, nFIDS);
    1011             :     }
    1012           1 :     else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1013             :                  "Feature type record index %d: no FOID field", iRecord)))
    1014             :     {
    1015           0 :         return false;
    1016             :     }
    1017             : 
    1018             :     // A FRID record might have a ATTR field, a INAS field (pointing to a IRID
    1019             :     // record), a FASC field (thus pointing to another FRID record), or any
    1020             :     // combination of the 3.
    1021             : 
    1022         789 :     return FillFeatureTypeGeometry(poRecord, iRecord, oFeature) &&
    1023         783 :            FillFeatureTypeMask(poRecord, iRecord, oFeature) &&
    1024         780 :            FillFeatureAttributes(oIndex, iRecord, ATTR_FIELD, oFeature) &&
    1025         780 :            FillFeatureAttributes(oIndex, iRecord, INAS_FIELD, oFeature) &&
    1026         780 :            FillFeatureAttributes(oIndex, iRecord, FASC_FIELD, oFeature) &&
    1027         780 :            FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, INAS_FIELD,
    1028        1572 :                                                 oFeature) &&
    1029         780 :            FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, FASC_FIELD,
    1030         789 :                                                 oFeature);
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                   ProcessUpdateRecordFeatureType()                   */
    1035             : /************************************************************************/
    1036             : 
    1037          19 : bool OGRS101Reader::ProcessUpdateRecordFeatureType(
    1038             :     const DDFRecord *poUpdateRecord, DDFRecord *poTargetRecord) const
    1039             : {
    1040          19 :     const auto poIDField = poUpdateRecord->GetField(0);
    1041          19 :     CPLAssert(poIDField);
    1042             : 
    1043             :     // Record name
    1044             :     const RecordName nRCNM =
    1045          19 :         poUpdateRecord->GetIntSubfield(poIDField, RCNM_SUBFIELD, 0);
    1046             : 
    1047             :     // Record identifier
    1048             :     const int nRCID =
    1049          19 :         poUpdateRecord->GetIntSubfield(poIDField, RCID_SUBFIELD, 0);
    1050             : 
    1051             :     // Deal with FOID field updates
    1052          19 :     if (const auto poFOIDFieldUpdate = poUpdateRecord->FindField(FOID_FIELD))
    1053             :     {
    1054          19 :         auto poFOIDFieldTarget = poTargetRecord->FindField(FOID_FIELD);
    1055          19 :         if (!poFOIDFieldTarget)
    1056             :         {
    1057           0 :             return EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1058             :                 "%s, RCNM=%d, RCID=%d: missing FOID field in "
    1059             :                 "target record",
    1060             :                 m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID));
    1061             :         }
    1062             : 
    1063          57 :         for (const char *pszSubFieldName :
    1064          76 :              {AGEN_SUBFIELD, FIDN_SUBFIELD, FIDS_SUBFIELD})
    1065             :         {
    1066          57 :             const int nVal = poUpdateRecord->GetIntSubfield(poFOIDFieldUpdate,
    1067             :                                                             pszSubFieldName, 0);
    1068          57 :             poTargetRecord->SetIntSubfield(FOID_FIELD, 0, pszSubFieldName, 0,
    1069             :                                            nVal);
    1070             :         }
    1071             :     }
    1072             : 
    1073             :     // Deal with SPAS field updates
    1074          38 :     const auto apoSPASFieldUpdates = poUpdateRecord->GetFields(SPAS_FIELD);
    1075          19 :     if (!apoSPASFieldUpdates.empty())
    1076             :     {
    1077             :         struct SPASField
    1078             :         {
    1079             :             int RRNM = 0;
    1080             :             int RRID = 0;
    1081             :             int ORNT = 0;
    1082             :             int SMIN = 0;
    1083             :             int SMAX = 0;
    1084             :             int SAUI = 0;
    1085             : 
    1086          46 :             static SPASField Read(const DDFRecord *poRecord,
    1087             :                                   const DDFField *poField, int i)
    1088             :             {
    1089          46 :                 SPASField f;
    1090          46 :                 f.RRNM = poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, i);
    1091          46 :                 f.RRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, i);
    1092          46 :                 f.ORNT = poRecord->GetIntSubfield(poField, ORNT_SUBFIELD, i);
    1093          46 :                 f.SMIN = poRecord->GetIntSubfield(poField, SMIN_SUBFIELD, i);
    1094          46 :                 f.SMAX = poRecord->GetIntSubfield(poField, SMAX_SUBFIELD, i);
    1095          46 :                 f.SAUI = poRecord->GetIntSubfield(poField, SAUI_SUBFIELD, i);
    1096          46 :                 return f;
    1097             :             }
    1098             :         };
    1099             : 
    1100          12 :         std::vector<SPASField> asSPASFields;
    1101             :         // Ingest the existing/target record(s)
    1102          24 :         for (auto *poField : poTargetRecord->GetFields(SPAS_FIELD))
    1103             :         {
    1104          12 :             const int nRepeatCount = poField->GetRepeatCount();
    1105          36 :             for (int i = 0; i < nRepeatCount; ++i)
    1106             :             {
    1107          24 :                 asSPASFields.push_back(
    1108          48 :                     SPASField::Read(poTargetRecord, poField, i));
    1109             :             }
    1110             : 
    1111          12 :             poTargetRecord->DeleteField(poField);
    1112             :         }
    1113             : 
    1114             :         // Apply the update record(s)
    1115          22 :         for (const auto *poSPASFieldUpdate : apoSPASFieldUpdates)
    1116             :         {
    1117          12 :             const int nUpdateRepeatCount = poSPASFieldUpdate->GetRepeatCount();
    1118          32 :             for (int i = 0; i < nUpdateRepeatCount; ++i)
    1119             :             {
    1120             :                 SPASField updateFld =
    1121          22 :                     SPASField::Read(poUpdateRecord, poSPASFieldUpdate, i);
    1122          22 :                 if (updateFld.SAUI == INSTRUCTION_INSERT)
    1123             :                 {
    1124          10 :                     asSPASFields.push_back(updateFld);
    1125             :                 }
    1126          12 :                 else if (updateFld.SAUI == INSTRUCTION_DELETE)
    1127             :                 {
    1128          10 :                     bool bMatchFound = false;
    1129          14 :                     for (size_t j = 0; j < asSPASFields.size(); ++j)
    1130             :                     {
    1131          20 :                         if (asSPASFields[j].RRNM == updateFld.RRNM &&
    1132           8 :                             asSPASFields[j].RRID == updateFld.RRID)
    1133             :                         {
    1134           8 :                             bMatchFound = true;
    1135           8 :                             asSPASFields.erase(asSPASFields.begin() + j);
    1136           8 :                             break;
    1137             :                         }
    1138             :                     }
    1139          12 :                     if (!bMatchFound &&
    1140           2 :                         !EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1141             :                             "%s, RCNM=%d, RCID=%d, SPAS iSubField=%d: update "
    1142             :                             "field references RRNM=%d, RRID=%d which does not "
    1143             :                             "exist in initial or previous update",
    1144             :                             m_osFilename.c_str(), static_cast<int>(nRCNM),
    1145             :                             nRCID, i, updateFld.RRNM, updateFld.RRID)))
    1146             :                     {
    1147           2 :                         return false;
    1148             :                     }
    1149             :                 }
    1150           2 :                 else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1151             :                              "%s, RCNM=%d, RCID=%d, SPAS iSubField=%d: invalid "
    1152             :                              "SAUI=%d",
    1153             :                              m_osFilename.c_str(), static_cast<int>(nRCNM),
    1154             :                              nRCID, i, updateFld.SAUI)))
    1155             :                 {
    1156           1 :                     return false;
    1157             :                 }
    1158             :             }
    1159             :         }
    1160             : 
    1161          10 :         if (!asSPASFields.empty())
    1162             :         {
    1163             :             const auto poSPASFieldDefn =
    1164          10 :                 m_oMainModule.FindFieldDefn(SPAS_FIELD);
    1165          10 :             if (!poSPASFieldDefn)
    1166             :             {
    1167           0 :                 return EMIT_ERROR("Cannot find SPAS field definition");
    1168             :             }
    1169          10 :             auto poSPASFieldTarget = poTargetRecord->AddField(poSPASFieldDefn);
    1170          10 :             CPLAssert(poSPASFieldTarget);
    1171          10 :             const auto *poSPASFieldUpdate = apoSPASFieldUpdates[0];
    1172          10 :             if (*(poSPASFieldTarget->GetFieldDefn()) !=
    1173          10 :                 *(poSPASFieldUpdate->GetFieldDefn()))
    1174             :             {
    1175           0 :                 return EMIT_ERROR("SPAS field definitions of update and target "
    1176             :                                   "records are different");
    1177             :             }
    1178             : 
    1179          32 :             for (int i = 0; i < static_cast<int>(asSPASFields.size()); ++i)
    1180             :             {
    1181          22 :                 poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, RRNM_SUBFIELD, i,
    1182          22 :                                                asSPASFields[i].RRNM);
    1183          22 :                 poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, RRID_SUBFIELD, i,
    1184          22 :                                                asSPASFields[i].RRID);
    1185          22 :                 poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, ORNT_SUBFIELD, i,
    1186          22 :                                                asSPASFields[i].ORNT);
    1187          22 :                 poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, SMIN_SUBFIELD, i,
    1188          22 :                                                asSPASFields[i].SMIN);
    1189          22 :                 poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, SMAX_SUBFIELD, i,
    1190          22 :                                                asSPASFields[i].SMAX);
    1191          22 :                 poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, SAUI_SUBFIELD, i,
    1192          22 :                                                asSPASFields[i].SAUI);
    1193             :             }
    1194             :         }
    1195             :     }
    1196             : 
    1197             :     // Deal with MASK field updates
    1198          34 :     const auto apoMASKFieldUpdates = poUpdateRecord->GetFields(MASK_FIELD);
    1199          17 :     if (!apoMASKFieldUpdates.empty())
    1200             :     {
    1201             :         struct MASKField
    1202             :         {
    1203             :             int RRNM = 0;
    1204             :             int RRID = 0;
    1205             :             int MIND = 0;
    1206             :             int MUIN = 0;
    1207             : 
    1208          64 :             static MASKField Read(const DDFRecord *poRecord,
    1209             :                                   const DDFField *poField, int i)
    1210             :             {
    1211          64 :                 MASKField f;
    1212          64 :                 f.RRNM = poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, i);
    1213          64 :                 f.RRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, i);
    1214          64 :                 f.MIND = poRecord->GetIntSubfield(poField, MIND_SUBFIELD, i);
    1215          64 :                 f.MUIN = poRecord->GetIntSubfield(poField, MUIN_SUBFIELD, i);
    1216          64 :                 return f;
    1217             :             }
    1218             :         };
    1219             : 
    1220          10 :         std::vector<MASKField> asMASKFields;
    1221             :         // Ingest the existing/target record(s)
    1222          30 :         for (auto *poField : poTargetRecord->GetFields(MASK_FIELD))
    1223             :         {
    1224          20 :             const int nRepeatCount = poField->GetRepeatCount();
    1225          50 :             for (int i = 0; i < nRepeatCount; ++i)
    1226             :             {
    1227          30 :                 asMASKFields.push_back(
    1228          60 :                     MASKField::Read(poTargetRecord, poField, i));
    1229             :             }
    1230             : 
    1231          20 :             poTargetRecord->DeleteField(poField);
    1232             :         }
    1233             : 
    1234             :         // Apply the update record(s)
    1235          18 :         for (const auto *poMASKFieldUpdate : apoMASKFieldUpdates)
    1236             :         {
    1237          10 :             const int nUpdateRepeatCount = poMASKFieldUpdate->GetRepeatCount();
    1238          42 :             for (int i = 0; i < nUpdateRepeatCount; ++i)
    1239             :             {
    1240             :                 MASKField updateFld =
    1241          34 :                     MASKField::Read(poUpdateRecord, poMASKFieldUpdate, i);
    1242          34 :                 if (updateFld.MUIN == INSTRUCTION_INSERT)
    1243             :                 {
    1244           8 :                     asMASKFields.push_back(updateFld);
    1245             :                 }
    1246          26 :                 else if (updateFld.MUIN == INSTRUCTION_DELETE)
    1247             :                 {
    1248          24 :                     bool bMatchFound = false;
    1249          34 :                     for (size_t j = 0; j < asMASKFields.size(); ++j)
    1250             :                     {
    1251          56 :                         if (asMASKFields[j].RRNM == updateFld.RRNM &&
    1252          24 :                             asMASKFields[j].RRID == updateFld.RRID)
    1253             :                         {
    1254          22 :                             bMatchFound = true;
    1255          22 :                             asMASKFields.erase(asMASKFields.begin() + j);
    1256          22 :                             break;
    1257             :                         }
    1258             :                     }
    1259          26 :                     if (!bMatchFound &&
    1260           2 :                         !EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1261             :                             "%s, RCNM=%d, RCID=%d, MASK iSubField=%d: update "
    1262             :                             "field references RRNM=%d, RRID=%d which does not "
    1263             :                             "exist in initial or previous update",
    1264             :                             m_osFilename.c_str(), static_cast<int>(nRCNM),
    1265             :                             nRCID, i, updateFld.RRNM, updateFld.RRID)))
    1266             :                     {
    1267           2 :                         return false;
    1268             :                     }
    1269             :                 }
    1270           2 :                 else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1271             :                              "%s, RCNM=%d, RCID=%d, MASK iSubField=%d: invalid "
    1272             :                              "MUIN=%d",
    1273             :                              m_osFilename.c_str(), static_cast<int>(nRCNM),
    1274             :                              nRCID, i, updateFld.MUIN)))
    1275             :                 {
    1276           1 :                     return false;
    1277             :                 }
    1278             :             }
    1279             :         }
    1280             : 
    1281           8 :         if (!asMASKFields.empty())
    1282             :         {
    1283             :             const auto poMASKFieldDefn =
    1284           8 :                 m_oMainModule.FindFieldDefn(MASK_FIELD);
    1285           8 :             if (!poMASKFieldDefn)
    1286             :             {
    1287           0 :                 return EMIT_ERROR("Cannot find MASK field definition");
    1288             :             }
    1289           8 :             auto poMASKFieldTarget = poTargetRecord->AddField(poMASKFieldDefn);
    1290           8 :             CPLAssert(poMASKFieldTarget);
    1291           8 :             const auto *poMASKFieldUpdate = apoMASKFieldUpdates[0];
    1292           8 :             if (*(poMASKFieldTarget->GetFieldDefn()) !=
    1293           8 :                 *(poMASKFieldUpdate->GetFieldDefn()))
    1294             :             {
    1295           0 :                 return EMIT_ERROR("MASK field definitions of update and target "
    1296             :                                   "records are different");
    1297             :             }
    1298             : 
    1299          18 :             for (int i = 0; i < static_cast<int>(asMASKFields.size()); ++i)
    1300             :             {
    1301          10 :                 poTargetRecord->SetIntSubfield(MASK_FIELD, 0, RRNM_SUBFIELD, i,
    1302          10 :                                                asMASKFields[i].RRNM);
    1303          10 :                 poTargetRecord->SetIntSubfield(MASK_FIELD, 0, RRID_SUBFIELD, i,
    1304          10 :                                                asMASKFields[i].RRID);
    1305          10 :                 poTargetRecord->SetIntSubfield(MASK_FIELD, 0, MIND_SUBFIELD, i,
    1306          10 :                                                asMASKFields[i].MIND);
    1307          10 :                 poTargetRecord->SetIntSubfield(MASK_FIELD, 0, MUIN_SUBFIELD, i,
    1308          10 :                                                asMASKFields[i].MUIN);
    1309             :             }
    1310             :         }
    1311             :     }
    1312             : 
    1313          15 :     return true;
    1314             : }

Generated by: LCOV version 1.14