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

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  S-101 driver
       4             :  * Purpose:  Implements OGRS101Reader
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_s101.h"
      14             : #include "ogrs101readerconstants.h"
      15             : 
      16             : #include <memory>
      17             : 
      18             : /************************************************************************/
      19             : /*                       GetMultiPointLayerName()                       */
      20             : /************************************************************************/
      21             : 
      22             : /* static */ std::string
      23         285 : OGRS101Reader::GetMultiPointLayerName(const OGRSpatialReference &oSRS)
      24             : {
      25         285 :     return oSRS.GetAxesCount() == 2
      26             :                ? "MultiPoint2D"
      27         855 :                : CPLSPrintf("MultiPoint3D_%s", LaunderCRSName(oSRS).c_str());
      28             : }
      29             : 
      30             : /************************************************************************/
      31             : /*                    CreateMultiPointFeatureDefns()                    */
      32             : /************************************************************************/
      33             : 
      34             : /** Create the feature definition(s) for the MultiPoint layer(s)
      35             :  *
      36             :  * There is a layer per CRS used by multipoints.
      37             :  */
      38         251 : bool OGRS101Reader::CreateMultiPointFeatureDefns()
      39             : {
      40         251 :     bool bError = false;
      41             : 
      42             :     m_oMapCRSIdToMultiPointRecordIdx =
      43         251 :         CreateMapCRSIdToRecordIdxForMultiPoints(bError);
      44         251 :     if (bError)
      45           2 :         return false;
      46         309 :     for (const auto &[nCRSId, anRecordIdx] : m_oMapCRSIdToMultiPointRecordIdx)
      47             :     {
      48          60 :         const auto &oSRS = m_oMapSRS[nCRSId];
      49          60 :         const bool bIs2D = nCRSId == HORIZONTAL_CRS_ID;
      50             :         auto poFDefn = OGRFeatureDefnRefCountedPtr::makeInstance(
      51          60 :             GetMultiPointLayerName(oSRS).c_str());
      52          60 :         poFDefn->SetGeomType(bIs2D ? wkbMultiPoint : wkbMultiPoint25D);
      53         120 :         poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(
      54         120 :             OGRSpatialReferenceRefCountedPtr::makeClone(&oSRS).get());
      55             :         {
      56         120 :             OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_ID, OFTInteger);
      57          60 :             poFDefn->AddFieldDefn(&oFieldDefn);
      58             :         }
      59             :         {
      60         120 :             OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_VERSION, OFTInteger);
      61          60 :             poFDefn->AddFieldDefn(&oFieldDefn);
      62             :         }
      63          60 :         if (!InferFeatureDefn(m_oMultiPointRecordIndex, MRID_FIELD, INAS_FIELD,
      64          60 :                               anRecordIdx, *poFDefn, m_oMapFieldDomains))
      65             :         {
      66           0 :             return false;
      67             :         }
      68          60 :         m_oMapMultiPointFeatureDefn[nCRSId] = std::move(poFDefn);
      69             :     }
      70             : 
      71         249 :     return true;
      72             : }
      73             : 
      74             : /************************************************************************/
      75             : /*                    GetCRSIdForMultiPointRecord()                     */
      76             : /************************************************************************/
      77             : 
      78             : /** Return the CRS id for a given MultiPoint record, or INVALID_CRS_ID on error */
      79             : OGRS101Reader::CRSId
      80         141 : OGRS101Reader::GetCRSIdForMultiPointRecord(const DDFRecord *poRecord,
      81             :                                            int iRecord, int nRecordID) const
      82             : {
      83         141 :     if (nRecordID < 0)
      84          75 :         nRecordID = poRecord->GetIntSubfield(MRID_FIELD, 0, RCID_SUBFIELD, 0);
      85             : 
      86          10 :     const auto GetErrorContext = [iRecord, nRecordID]()
      87             :     {
      88           5 :         if (iRecord >= 0)
      89           5 :             return CPLSPrintf("Record index=%d of MRID", iRecord);
      90             :         else
      91           0 :             return CPLSPrintf("Record ID=%d of MRID", nRecordID);
      92         141 :     };
      93             : 
      94         141 :     if (poRecord->FindField(C3IL_FIELD))
      95             :     {
      96             :         const CRSId nVCID =
      97          20 :             poRecord->GetIntSubfield(C3IL_FIELD, 0, VCID_SUBFIELD, 0);
      98          20 :         if (nVCID == HORIZONTAL_CRS_ID)
      99             :         {
     100           2 :             CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
     101             :                 CPLSPrintf("%s: VCID subfield = %d of C3IL "
     102             :                            "field points to a non-3D CRS.",
     103             :                            GetErrorContext(), static_cast<int>(nVCID))));
     104           2 :             return INVALID_CRS_ID;
     105             :         }
     106          18 :         else if (!cpl::contains(m_oMapSRS, nVCID))
     107             :         {
     108           2 :             CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
     109             :                 CPLSPrintf("%s: Unknown value %d for VCID subfield of C3IL "
     110             :                            "field.",
     111             :                            GetErrorContext(), static_cast<int>(nVCID))));
     112           2 :             return INVALID_CRS_ID;
     113             :         }
     114             :         else
     115             :         {
     116          16 :             return nVCID;
     117             :         }
     118             :     }
     119         121 :     else if (poRecord->FindField(C2IL_FIELD))
     120             :     {
     121         120 :         return HORIZONTAL_CRS_ID;
     122             :     }
     123             :     else
     124             :     {
     125           1 :         CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
     126             :             CPLSPrintf("%s: No C2IL or C3IL field found.", GetErrorContext())));
     127           1 :         return INVALID_CRS_ID;
     128             :     }
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*              CreateMapCRSIdToRecordIdxForMultiPoints()               */
     133             : /************************************************************************/
     134             : 
     135             : /** Browse through m_oMultiPointRecordIndex to identify which record belongs to
     136             :  * each CRS and create a map from each CRS id to the record indices that use
     137             :  * it.
     138             :  */
     139             : std::map<OGRS101Reader::CRSId, std::vector<int>>
     140         251 : OGRS101Reader::CreateMapCRSIdToRecordIdxForMultiPoints(bool &bError) const
     141             : {
     142         502 :     std::map<OGRS101Reader::CRSId, std::vector<int>> map;
     143             : 
     144         251 :     const int nRecords = m_oMultiPointRecordIndex.GetCount();
     145         324 :     for (int iRecord = 0; iRecord < nRecords; ++iRecord)
     146             :     {
     147          75 :         const auto poRecord = m_oMultiPointRecordIndex.GetByIndex(iRecord);
     148             :         const CRSId nCRSId = GetCRSIdForMultiPointRecord(poRecord, iRecord,
     149          75 :                                                          /* nRecordID = */ -1);
     150          75 :         if (nCRSId == INVALID_CRS_ID)
     151             :         {
     152           5 :             if (m_bStrict)
     153             :             {
     154           2 :                 bError = true;
     155           2 :                 return {};
     156             :             }
     157             :         }
     158             :         else
     159             :         {
     160          70 :             map[nCRSId].push_back(iRecord);
     161             :         }
     162             :     }
     163             : 
     164         249 :     return map;
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                       ReadMultiPointGeometry()                       */
     169             : /************************************************************************/
     170             : 
     171             : std::unique_ptr<OGRMultiPoint>
     172         387 : OGRS101Reader::ReadMultiPointGeometry(const DDFRecord *poRecord, int iRecord,
     173             :                                       int nRecordID,
     174             :                                       const OGRSpatialReference *poSRS) const
     175             : {
     176         387 :     const bool bIs3D = poRecord->FindField(C3IL_FIELD) != nullptr;
     177         387 :     const char *pszCoordFieldName = bIs3D ? C3IL_FIELD : C2IL_FIELD;
     178         774 :     const auto apoCoordFields = poRecord->GetFields(pszCoordFieldName);
     179         387 :     if (apoCoordFields.empty())
     180           0 :         return nullptr;
     181             : 
     182         774 :     auto poMP = std::make_unique<OGRMultiPoint>();
     183         387 :     poMP->assignSpatialReference(poSRS);
     184         772 :     for (const auto *poCoordField : apoCoordFields)
     185             :     {
     186             :         const int nCoordCount =
     187          86 :             bIs3D && poCoordField->GetParts().size() == 2
     188         473 :                 ? poCoordField->GetParts()[1]->GetRepeatCount()
     189         301 :                 : poCoordField->GetRepeatCount();
     190             : 
     191        1084 :         for (int iPnt = 0; iPnt < nCoordCount; ++iPnt)
     192             :         {
     193             :             auto poPoint = ReadPointGeometryInternal(
     194             :                 poRecord, iRecord, nRecordID, iPnt, poSRS, bIs3D, poCoordField,
     195         699 :                 MRID_FIELD);
     196         699 :             if (!poPoint)
     197           2 :                 return nullptr;
     198         697 :             poMP->addGeometry(std::move(poPoint));
     199             :         }
     200             :     }
     201         385 :     return poMP;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                       FillFeatureMultiPoint()                        */
     206             : /************************************************************************/
     207             : 
     208             : /** Fill the content of the provided feature from the identified record
     209             :  * (of m_oMultiPointRecordIndex).
     210             :  */
     211         223 : bool OGRS101Reader::FillFeatureMultiPoint(const DDFRecordIndex &oIndex,
     212             :                                           int iRecord,
     213             :                                           OGRFeature &oFeature) const
     214             : {
     215         223 :     const auto poRecord = oIndex.GetByIndex(iRecord);
     216         223 :     CPLAssert(poRecord);
     217             : 
     218             :     const OGRSpatialReference *poSRS =
     219         223 :         oFeature.GetDefnRef()->GetGeomFieldDefn(0)->GetSpatialRef();
     220             :     auto poMP =
     221         446 :         ReadMultiPointGeometry(poRecord, iRecord, /* nRecordID = */ -1, poSRS);
     222         223 :     if (poMP)
     223             :     {
     224         221 :         oFeature.SetGeometry(std::move(poMP));
     225             :     }
     226           2 :     else if (m_bStrict)
     227           2 :         return false;
     228             : 
     229         442 :     return FillFeatureAttributes(oIndex, iRecord, INAS_FIELD, oFeature) &&
     230         221 :            FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, INAS_FIELD,
     231         221 :                                                 oFeature);
     232             : }

Generated by: LCOV version 1.14