LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101readercurve.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 106 107 99.1 %
Date: 2026-05-08 18:52:02 Functions: 4 4 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 <memory>
      17             : 
      18             : /************************************************************************/
      19             : /*                       CreateCurveFeatureDefn()                       */
      20             : /************************************************************************/
      21             : 
      22             : /** Create the feature definition for the Curve layer
      23             :  */
      24         249 : bool OGRS101Reader::CreateCurveFeatureDefn()
      25             : {
      26         249 :     if (m_oCurveRecordIndex.GetCount() > 0)
      27             :     {
      28             :         m_poFeatureDefnCurve =
      29         113 :             OGRFeatureDefnRefCountedPtr::makeInstance(OGR_LAYER_NAME_CURVE);
      30         113 :         m_poFeatureDefnCurve->SetGeomType(wkbLineString);
      31         113 :         auto oSRSIter = m_oMapSRS.find(HORIZONTAL_CRS_ID);
      32         113 :         if (oSRSIter != m_oMapSRS.end())
      33             :         {
      34         226 :             m_poFeatureDefnCurve->GetGeomFieldDefn(0)->SetSpatialRef(
      35         226 :                 OGRSpatialReferenceRefCountedPtr::makeClone(&oSRSIter->second)
      36         113 :                     .get());
      37             :         }
      38             :         {
      39         226 :             OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_ID, OFTInteger);
      40         113 :             m_poFeatureDefnCurve->AddFieldDefn(&oFieldDefn);
      41             :         }
      42             :         {
      43         226 :             OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_VERSION, OFTInteger);
      44         113 :             m_poFeatureDefnCurve->AddFieldDefn(&oFieldDefn);
      45             :         }
      46         113 :         if (!InferFeatureDefn(m_oCurveRecordIndex, CRID_FIELD, INAS_FIELD, {},
      47         113 :                               *m_poFeatureDefnCurve, m_oMapFieldDomains))
      48             :         {
      49           1 :             return false;
      50             :         }
      51             :     }
      52             : 
      53         248 :     return true;
      54             : }
      55             : 
      56             : /************************************************************************/
      57             : /*                         ReadCurveGeometry()                          */
      58             : /************************************************************************/
      59             : 
      60             : std::unique_ptr<OGRLineString>
      61        2031 : OGRS101Reader::ReadCurveGeometry(const DDFRecord *poRecord, int iRecord,
      62             :                                  int nRecordID,
      63             :                                  const OGRSpatialReference *poSRS) const
      64             : {
      65        2031 :     const DDFField *poSEGHField = poRecord->FindField(SEGH_FIELD);
      66        2031 :     if (poSEGHField)
      67             :     {
      68        2031 :         constexpr const char *INTP_SUBFIELD = "INTP";
      69             :         const int nINTP =
      70        2031 :             poRecord->GetIntSubfield(SEGH_FIELD, 0, INTP_SUBFIELD, 0);
      71        2031 :         constexpr int INTERPOLATION_LOXODROMIC = 4;
      72        2039 :         if (nINTP != INTERPOLATION_LOXODROMIC &&
      73           8 :             !EMIT_ERROR_OR_WARNING(
      74             :                 CPLSPrintf("Record index %d of CRID: Invalid value for INTP "
      75             :                            "subfield of SEGH field: "
      76             :                            "got %d, expected %d.",
      77             :                            iRecord, nINTP, INTERPOLATION_LOXODROMIC)))
      78             :         {
      79           2 :             return nullptr;
      80             :         }
      81             :     }
      82             : 
      83        4058 :     const auto apoCoordFields = poRecord->GetFields(C2IL_FIELD);
      84        2029 :     if (apoCoordFields.empty())
      85           0 :         return nullptr;
      86             : 
      87        4058 :     auto poLS = std::make_unique<OGRLineString>();
      88        2029 :     poLS->assignSpatialReference(poSRS);
      89        2029 :     int nCoordCount = 0;
      90        4058 :     for (const auto *poCoordField : apoCoordFields)
      91             :     {
      92        2029 :         const int nCoordFieldCount = poCoordField->GetRepeatCount();
      93        2029 :         nCoordCount += nCoordFieldCount;
      94             :     }
      95        2029 :     poLS->setNumPoints(nCoordCount);
      96        2029 :     int iPnt = 0;
      97        4057 :     for (const auto *poCoordField : apoCoordFields)
      98             :     {
      99        2029 :         const int nCoordFieldCount = poCoordField->GetRepeatCount();
     100       10952 :         for (int iPntThisField = 0; iPntThisField < nCoordFieldCount;
     101             :              ++iPntThisField)
     102             :         {
     103             :             auto poPoint = ReadPointGeometryInternal(
     104             :                 poRecord, iRecord, nRecordID, iPntThisField, poSRS,
     105        8924 :                 /* bIs3D = */ false, poCoordField, CRID_FIELD);
     106        8924 :             if (!poPoint)
     107           1 :                 return nullptr;
     108        8923 :             poLS->setPoint(iPnt, poPoint.get());
     109        8923 :             ++iPnt;
     110             :         }
     111             :     }
     112             : 
     113        2028 :     const DDFField *poPTASField = poRecord->FindField(PTAS_FIELD);
     114        2028 :     if (poPTASField)
     115             :     {
     116        2028 :         const int nPTASMembers = poPTASField->GetRepeatCount();
     117        2028 :         constexpr const char *TOPI_SUBFIELD = "TOPI";
     118        2028 :         if (nPTASMembers == 1 || nPTASMembers == 2)
     119             :         {
     120        4551 :             for (int iPTAS = 0; iPTAS < nPTASMembers; ++iPTAS)
     121             :             {
     122             :                 const auto GetIntSubfield =
     123        7594 :                     [poRecord, poPTASField, iPTAS](const char *pszSubFieldName)
     124             :                 {
     125        7594 :                     return poRecord->GetIntSubfield(poPTASField,
     126        7594 :                                                     pszSubFieldName, iPTAS);
     127        2533 :                 };
     128             : 
     129        2533 :                 const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
     130        2533 :                 constexpr RecordName nExpectedRRNM = RECORD_NAME_POINT;
     131        2536 :                 if (nRRNM != nExpectedRRNM &&
     132           3 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     133             :                         "Record index %d of CRID: Invalid value for RRNM "
     134             :                         "subfield of %d instance of PTAS field: "
     135             :                         "got %d, expected %d.",
     136             :                         iRecord, iPTAS, static_cast<int>(nRRNM),
     137             :                         static_cast<int>(nExpectedRRNM))))
     138             :                 {
     139           7 :                     return nullptr;
     140             :                 }
     141             : 
     142        2532 :                 const int nTOPI = GetIntSubfield(TOPI_SUBFIELD);
     143        2532 :                 constexpr int TOPOLOGY_INDICATOR_BEGINNING_POINT = 1;
     144        2532 :                 constexpr int TOPOLOGY_INDICATOR_END_POINT = 2;
     145        2532 :                 constexpr int TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT = 3;
     146        2532 :                 const int nExpectedTOPI =
     147             :                     nPTASMembers == 1
     148        2532 :                         ? TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT
     149             :                     : iPTAS == 0 ? TOPOLOGY_INDICATOR_BEGINNING_POINT
     150             :                                  : TOPOLOGY_INDICATOR_END_POINT;
     151             : 
     152        2541 :                 if (nTOPI != nExpectedTOPI &&
     153           9 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     154             :                         "Record index %d of CRID: Invalid value for TOPI "
     155             :                         "subfield of %d instance of PTAS field: "
     156             :                         "got %d, expected %d.",
     157             :                         iRecord, iPTAS, nTOPI, nExpectedTOPI)))
     158             :                 {
     159           3 :                     return nullptr;
     160             :                 }
     161             : 
     162        2529 :                 const int nRRID = GetIntSubfield(RRID_SUBFIELD);
     163             :                 const auto poPointRecord =
     164        2529 :                     m_oPointRecordIndex.FindRecord(nRRID);
     165        2532 :                 if (!poPointRecord &&
     166           3 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     167             :                         "Record index %d of CRID: No point record matching "
     168             :                         "RRID=%d value of %d instance of PTAS field.",
     169             :                         iRecord, nRRID, iPTAS)))
     170             :                 {
     171           1 :                     return nullptr;
     172             :                 }
     173        2528 :                 if (poPointRecord)
     174             :                 {
     175        2526 :                     if (nTOPI == TOPOLOGY_INDICATOR_BEGINNING_POINT ||
     176             :                         nTOPI == TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT)
     177             :                     {
     178        2015 :                         if (poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
     179             :                                                           XCOO_SUBFIELD, 0) !=
     180        2015 :                                 poRecord->GetIntSubfield(C2IL_FIELD, 0,
     181        4020 :                                                          XCOO_SUBFIELD, 0) ||
     182        2005 :                             poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
     183             :                                                           YCOO_SUBFIELD, 0) !=
     184        2005 :                                 poRecord->GetIntSubfield(C2IL_FIELD, 0,
     185             :                                                          YCOO_SUBFIELD, 0))
     186             :                         {
     187          15 :                             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     188             :                                     "Record index %d of CRID: Point record %d "
     189             :                                     "pointed by %d instance of PTAS field does "
     190             :                                     "not match first point of curve.",
     191             :                                     iRecord, nRRID, iPTAS)))
     192             :                             {
     193           1 :                                 return nullptr;
     194             :                             }
     195             :                         }
     196             :                     }
     197             : 
     198        2525 :                     if (nTOPI == TOPOLOGY_INDICATOR_END_POINT ||
     199             :                         nTOPI == TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT)
     200             :                     {
     201        2015 :                         if (poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
     202             :                                                           XCOO_SUBFIELD, 0) !=
     203        2015 :                                 poRecord->GetIntSubfield(C2IL_FIELD, 0,
     204             :                                                          XCOO_SUBFIELD,
     205        4021 :                                                          nCoordCount - 1) ||
     206        2006 :                             poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
     207             :                                                           YCOO_SUBFIELD, 0) !=
     208        2006 :                                 poRecord->GetIntSubfield(C2IL_FIELD, 0,
     209             :                                                          YCOO_SUBFIELD,
     210             :                                                          nCoordCount - 1))
     211             :                         {
     212          12 :                             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     213             :                                     "Record index %d of CRID: Point record %d "
     214             :                                     "pointed by %d instance of PTAS field does "
     215             :                                     "not match end point of curve.",
     216             :                                     iRecord, nRRID, iPTAS)))
     217             :                             {
     218           1 :                                 return nullptr;
     219             :                             }
     220             :                         }
     221             :                     }
     222             :                 }
     223        2018 :             }
     224             :         }
     225             :         else
     226             :         {
     227           3 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     228             :                     "Record index %d of CRID: Invalid repeat count for "
     229             :                     "PTAS field: got %d, expected 1 or 2.",
     230             :                     iRecord, nPTASMembers)))
     231             :             {
     232           1 :                 return nullptr;
     233             :             }
     234             :         }
     235             :     }
     236             : 
     237        2020 :     return poLS;
     238             : }
     239             : 
     240             : /************************************************************************/
     241             : /*                          FillFeatureCurve()                          */
     242             : /************************************************************************/
     243             : 
     244             : /** Fill the content of the provided feature from the identified record
     245             :  * (of m_oCurveRecordIndex).
     246             :  */
     247         539 : bool OGRS101Reader::FillFeatureCurve(const DDFRecordIndex &oIndex, int iRecord,
     248             :                                      OGRFeature &oFeature) const
     249             : {
     250         539 :     const auto poRecord = oIndex.GetByIndex(iRecord);
     251         539 :     CPLAssert(poRecord);
     252             : 
     253             :     const OGRSpatialReference *poSRS =
     254         539 :         oFeature.GetDefnRef()->GetGeomFieldDefn(0)->GetSpatialRef();
     255             :     auto poLS =
     256        1078 :         ReadCurveGeometry(poRecord, iRecord, /* nRecordID = */ -1, poSRS);
     257         539 :     if (!poLS)
     258             :     {
     259          11 :         if (m_bStrict)
     260          11 :             return false;  // error message already emitted
     261             :     }
     262             :     else
     263             :     {
     264         528 :         oFeature.SetGeometry(std::move(poLS));
     265             :     }
     266             : 
     267        1056 :     return FillFeatureAttributes(oIndex, iRecord, INAS_FIELD, oFeature) &&
     268         528 :            FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, INAS_FIELD,
     269         528 :                                                 oFeature);
     270             : }

Generated by: LCOV version 1.14