LCOV - code coverage report
Current view: top level - frmts/pds - pds4vector.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1249 1429 87.4 %
Date: 2026-09-14 01:27:28 Functions: 56 59 94.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PDS 4 Driver; Planetary Data System Format
       4             :  * Purpose:  Implementation of PDS4Dataset
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2019, Hobu Inc
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "pds4dataset.h"
      14             : #include "ogrvrtgeometrytypes.h"
      15             : 
      16             : #include "ogr_p.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cassert>
      20             : 
      21             : PDS4TableLayerInterface::~PDS4TableLayerInterface() = default;
      22             : 
      23             : /************************************************************************/
      24             : /* ==================================================================== */
      25             : /*                        PDS4TableBaseLayer                            */
      26             : /* ==================================================================== */
      27             : /************************************************************************/
      28             : 
      29         166 : PDS4TableBaseLayer::PDS4TableBaseLayer(PDS4Dataset *poDS, const char *pszName,
      30         166 :                                        const char *pszFilename, bool bUpdate)
      31             :     : m_bUpdate(bUpdate), m_poDS(poDS),
      32         166 :       m_poRawFeatureDefn(new OGRFeatureDefn(pszName)),
      33         332 :       m_poFeatureDefn(new OGRFeatureDefn(pszName)), m_osFilename(pszFilename)
      34             : {
      35         166 :     m_poRawFeatureDefn->SetGeomType(wkbNone);
      36         166 :     m_poRawFeatureDefn->Reference();
      37         166 :     m_poFeatureDefn->SetGeomType(wkbNone);
      38         166 :     m_poFeatureDefn->Reference();
      39         166 :     SetDescription(pszName);
      40             : 
      41         166 :     m_bKeepGeomColmuns =
      42         166 :         CPLFetchBool(m_poDS->GetOpenOptions(), "KEEP_GEOM_COLUMNS", false);
      43         166 : }
      44             : 
      45             : /************************************************************************/
      46             : /*                           SetSpatialRef()                            */
      47             : /************************************************************************/
      48             : 
      49           3 : void PDS4TableBaseLayer::SetSpatialRef(OGRSpatialReference *poSRS)
      50             : {
      51           3 :     if (GetGeomType() != wkbNone)
      52             :     {
      53           3 :         GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
      54             :     }
      55           3 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                        ~PDS4TableBaseLayer()                         */
      59             : /************************************************************************/
      60             : 
      61         166 : PDS4TableBaseLayer::~PDS4TableBaseLayer()
      62             : {
      63         166 :     m_poFeatureDefn->Release();
      64         166 :     m_poRawFeatureDefn->Release();
      65         166 :     if (m_fp)
      66         165 :         VSIFCloseL(m_fp);
      67         166 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                            RenameFileTo()                            */
      71             : /************************************************************************/
      72             : 
      73           3 : bool PDS4TableBaseLayer::RenameFileTo(const char *pszNewName)
      74             : {
      75           3 :     if (m_fp)
      76           3 :         VSIFCloseL(m_fp);
      77           3 :     m_fp = nullptr;
      78           6 :     CPLString osBackup(pszNewName);
      79           3 :     osBackup += ".bak";
      80           3 :     VSIRename(pszNewName, osBackup);
      81           3 :     bool bSuccess = VSIRename(m_osFilename, pszNewName) == 0;
      82           3 :     if (bSuccess)
      83             :     {
      84           3 :         m_fp = VSIFOpenL(pszNewName, "rb+");
      85           3 :         if (!m_fp)
      86             :         {
      87           0 :             VSIRename(osBackup, pszNewName);
      88           0 :             return false;
      89             :         }
      90             : 
      91           3 :         m_osFilename = pszNewName;
      92           3 :         VSIUnlink(osBackup);
      93           3 :         return true;
      94             :     }
      95             :     else
      96             :     {
      97           0 :         VSIRename(osBackup, pszNewName);
      98           0 :         return false;
      99             :     }
     100             : }
     101             : 
     102             : /************************************************************************/
     103             : /*                            GetFileList()                             */
     104             : /************************************************************************/
     105             : 
     106          64 : char **PDS4TableBaseLayer::GetFileList() const
     107             : {
     108          64 :     return CSLAddString(nullptr, GetFileName());
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                          GetFeatureCount()                           */
     113             : /************************************************************************/
     114             : 
     115         106 : GIntBig PDS4TableBaseLayer::GetFeatureCount(int bForce)
     116             : {
     117         106 :     if (m_poAttrQuery != nullptr || m_poFilterGeom != nullptr)
     118             :     {
     119           6 :         return OGRLayer::GetFeatureCount(bForce);
     120             :     }
     121         100 :     return m_nFeatureCount;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                           SetupGeomField()                           */
     126             : /************************************************************************/
     127             : 
     128          97 : void PDS4TableBaseLayer::SetupGeomField()
     129             : {
     130          97 :     const char *const *papszOpenOptions = m_poDS->GetOpenOptions();
     131          97 :     const char *pszWKT = CSLFetchNameValue(papszOpenOptions, "WKT");
     132         194 :     if (pszWKT == nullptr &&
     133         140 :         (m_iWKT = m_poRawFeatureDefn->GetFieldIndex("WKT")) >= 0 &&
     134          43 :         m_poRawFeatureDefn->GetFieldDefn(m_iWKT)->GetType() == OFTString)
     135             :     {
     136          43 :         pszWKT = "WKT";
     137             :     }
     138             :     else
     139             :     {
     140          54 :         m_iWKT = -1;
     141             :     }
     142          97 :     if (pszWKT && !EQUAL(pszWKT, ""))
     143             :     {
     144          43 :         m_iWKT = m_poRawFeatureDefn->GetFieldIndex(pszWKT);
     145          43 :         if (m_iWKT < 0)
     146             :         {
     147           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Unknown field %s", pszWKT);
     148             :         }
     149          43 :         else if (m_poRawFeatureDefn->GetFieldDefn(m_iWKT)->GetType() !=
     150             :                  OFTString)
     151             :         {
     152           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     153             :                      "The %s field should be of type String", pszWKT);
     154             :         }
     155             :         else
     156             :         {
     157          43 :             m_poFeatureDefn->SetGeomType(wkbUnknown);
     158             :         }
     159             :     }
     160             :     else
     161             :     {
     162          54 :         const char *pszLat = CSLFetchNameValue(papszOpenOptions, "LAT");
     163          54 :         const char *pszLong = CSLFetchNameValue(papszOpenOptions, "LONG");
     164          54 :         if (pszLat == nullptr && pszLong == nullptr &&
     165          54 :             (m_iLatField = m_poRawFeatureDefn->GetFieldIndex("Latitude")) >=
     166          11 :                 0 &&
     167          11 :             (m_iLongField = m_poRawFeatureDefn->GetFieldIndex("Longitude")) >=
     168          11 :                 0 &&
     169          11 :             m_poRawFeatureDefn->GetFieldDefn(m_iLatField)->GetType() ==
     170         108 :                 OFTReal &&
     171          11 :             m_poRawFeatureDefn->GetFieldDefn(m_iLongField)->GetType() ==
     172             :                 OFTReal)
     173             :         {
     174          11 :             pszLat = "Latitude";
     175          11 :             pszLong = "Longitude";
     176             :         }
     177             :         else
     178             :         {
     179          43 :             m_iLatField = -1;
     180          43 :             m_iLongField = -1;
     181             :         }
     182          54 :         if (pszLat && pszLong && !EQUAL(pszLat, "") && !EQUAL(pszLong, ""))
     183             :         {
     184          11 :             m_iLatField = m_poRawFeatureDefn->GetFieldIndex(pszLat);
     185          11 :             m_iLongField = m_poRawFeatureDefn->GetFieldIndex(pszLong);
     186          11 :             if (m_iLatField < 0)
     187             :             {
     188           0 :                 CPLError(CE_Warning, CPLE_AppDefined, "Unknown field %s",
     189             :                          pszLat);
     190             :             }
     191          11 :             else if (m_poRawFeatureDefn->GetFieldDefn(m_iLatField)->GetType() !=
     192             :                      OFTReal)
     193             :             {
     194           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     195             :                          "The %s field should be of type Real", pszLat);
     196           0 :                 m_iLatField = -1;
     197             :             }
     198          11 :             if (m_iLongField < 0)
     199             :             {
     200           0 :                 CPLError(CE_Warning, CPLE_AppDefined, "Unknown field %s",
     201             :                          pszLong);
     202             :             }
     203          22 :             else if (m_poRawFeatureDefn->GetFieldDefn(m_iLongField)
     204          11 :                          ->GetType() != OFTReal)
     205             :             {
     206           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     207             :                          "The %s field should be of type Real", pszLong);
     208           0 :                 m_iLongField = -1;
     209             :             }
     210          11 :             if (m_iLatField < 0 || m_iLongField < 0)
     211             :             {
     212           0 :                 m_iLatField = -1;
     213           0 :                 m_iLongField = -1;
     214             :             }
     215             :             else
     216             :             {
     217          11 :                 const char *pszAlt = CSLFetchNameValue(papszOpenOptions, "ALT");
     218          22 :                 if (pszAlt == nullptr &&
     219          11 :                     (m_iAltField =
     220          22 :                          m_poRawFeatureDefn->GetFieldIndex("Altitude")) >= 0 &&
     221          11 :                     m_poRawFeatureDefn->GetFieldDefn(m_iAltField)->GetType() ==
     222             :                         OFTReal)
     223             :                 {
     224          11 :                     pszAlt = "Altitude";
     225             :                 }
     226             :                 else
     227             :                 {
     228           0 :                     m_iAltField = -1;
     229             :                 }
     230          11 :                 if (pszAlt && !EQUAL(pszAlt, ""))
     231             :                 {
     232          11 :                     m_iAltField = m_poRawFeatureDefn->GetFieldIndex(pszAlt);
     233          11 :                     if (m_iAltField < 0)
     234             :                     {
     235           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     236             :                                  "Unknown field %s", pszAlt);
     237             :                     }
     238          22 :                     else if (m_poRawFeatureDefn->GetFieldDefn(m_iAltField)
     239          11 :                                  ->GetType() != OFTReal)
     240             :                     {
     241           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     242             :                                  "The %s field should be of type Real", pszAlt);
     243           0 :                         m_iAltField = -1;
     244             :                     }
     245             :                 }
     246          11 :                 m_poFeatureDefn->SetGeomType(m_iAltField >= 0 ? wkbPoint25D
     247          11 :                                                               : wkbPoint);
     248             :             }
     249             :         }
     250             :     }
     251             : 
     252         948 :     for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
     253             :     {
     254         851 :         if (!m_bKeepGeomColmuns && (i == m_iWKT || i == m_iLatField ||
     255         797 :                                     i == m_iLongField || i == m_iAltField))
     256             :         {
     257             :             // do nothing;
     258             :         }
     259             :         else
     260             :         {
     261         775 :             m_poFeatureDefn->AddFieldDefn(m_poRawFeatureDefn->GetFieldDefn(i));
     262             :         }
     263             :     }
     264          97 : }
     265             : 
     266             : /************************************************************************/
     267             : /*                       AddGeometryFromFields()                        */
     268             : /************************************************************************/
     269             : 
     270         869 : OGRFeature *PDS4TableBaseLayer::AddGeometryFromFields(OGRFeature *poRawFeature)
     271             : {
     272         869 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
     273         869 :     poFeature->SetFID(poRawFeature->GetFID());
     274       11793 :     for (int i = 0, j = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
     275             :     {
     276       10924 :         if (!m_bKeepGeomColmuns && (i == m_iWKT || i == m_iLatField ||
     277       10070 :                                     i == m_iLongField || i == m_iAltField))
     278             :         {
     279             :             // do nothing;
     280             :         }
     281             :         else
     282             :         {
     283        9272 :             poFeature->SetField(j, poRawFeature->GetRawFieldRef(i));
     284        9272 :             j++;
     285             :         }
     286             :     }
     287             : 
     288         869 :     if (m_iWKT >= 0)
     289             :     {
     290         455 :         const char *pszWKT = poRawFeature->GetFieldAsString(m_iWKT);
     291         455 :         if (pszWKT && pszWKT[0] != '\0')
     292             :         {
     293         439 :             OGRGeometry *poGeom = nullptr;
     294         439 :             OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom);
     295         439 :             if (poGeom)
     296             :             {
     297         439 :                 poGeom->assignSpatialReference(GetSpatialRef());
     298         439 :                 poFeature->SetGeometryDirectly(poGeom);
     299             :             }
     300             :         }
     301             :     }
     302         399 :     else if (m_iLatField >= 0 && m_iLongField >= 0 &&
     303        1210 :              poRawFeature->IsFieldSetAndNotNull(m_iLatField) &&
     304         397 :              poRawFeature->IsFieldSetAndNotNull(m_iLongField))
     305             :     {
     306         397 :         double dfLat = poRawFeature->GetFieldAsDouble(m_iLatField);
     307         397 :         double dfLong = poRawFeature->GetFieldAsDouble(m_iLongField);
     308             :         OGRPoint *poPoint;
     309         397 :         if (m_iAltField >= 0 && poRawFeature->IsFieldSetAndNotNull(m_iAltField))
     310             :         {
     311         397 :             double dfAlt = poRawFeature->GetFieldAsDouble(m_iAltField);
     312         397 :             poPoint = new OGRPoint(dfLong, dfLat, dfAlt);
     313             :         }
     314             :         else
     315             :         {
     316           0 :             poPoint = new OGRPoint(dfLong, dfLat);
     317             :         }
     318         397 :         poPoint->assignSpatialReference(GetSpatialRef());
     319         397 :         poFeature->SetGeometryDirectly(poPoint);
     320             :     }
     321         869 :     return poFeature;
     322             : }
     323             : 
     324             : /************************************************************************/
     325             : /*                       AddFieldsFromGeometry()                        */
     326             : /************************************************************************/
     327             : 
     328         119 : OGRFeature *PDS4TableBaseLayer::AddFieldsFromGeometry(OGRFeature *poFeature)
     329             : {
     330         119 :     OGRFeature *poRawFeature = new OGRFeature(m_poRawFeatureDefn);
     331         938 :     for (int i = 0, j = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
     332             :     {
     333         819 :         if (!m_bKeepGeomColmuns && (i == m_iWKT || i == m_iLatField ||
     334         715 :                                     i == m_iLongField || i == m_iAltField))
     335             :         {
     336             :             // do nothing;
     337             :         }
     338             :         else
     339             :         {
     340         689 :             poRawFeature->SetField(i, poFeature->GetRawFieldRef(j));
     341         689 :             j++;
     342             :         }
     343             :     }
     344             : 
     345         119 :     auto poGeom = poFeature->GetGeometryRef();
     346         119 :     if (poGeom)
     347             :     {
     348          84 :         if (m_iLongField >= 0 && m_iLatField >= 0 &&
     349          12 :             wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     350             :         {
     351          12 :             auto poPoint = poGeom->toPoint();
     352          12 :             poRawFeature->SetField(m_iLongField, poPoint->getX());
     353          12 :             poRawFeature->SetField(m_iLatField, poPoint->getY());
     354          12 :             if (m_iAltField >= 0 && poGeom->getGeometryType() == wkbPoint25D)
     355             :             {
     356          12 :                 poRawFeature->SetField(m_iAltField, poPoint->getZ());
     357             :             }
     358             :         }
     359          60 :         else if (m_iWKT >= 0)
     360             :         {
     361          60 :             char *pszWKT = nullptr;
     362          60 :             poGeom->exportToWkt(&pszWKT);
     363          60 :             if (pszWKT)
     364             :             {
     365          60 :                 poRawFeature->SetField(m_iWKT, pszWKT);
     366             :             }
     367          60 :             CPLFree(pszWKT);
     368             :         }
     369             :     }
     370         119 :     return poRawFeature;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                          MarkHeaderDirty()                           */
     375             : /************************************************************************/
     376             : 
     377         338 : void PDS4TableBaseLayer::MarkHeaderDirty()
     378             : {
     379         338 :     m_bDirtyHeader = true;
     380         338 :     m_poDS->MarkHeaderDirty();
     381         338 : }
     382             : 
     383             : /************************************************************************/
     384             : /*            RefreshFileAreaObservationalBeginningCommon()             */
     385             : /************************************************************************/
     386             : 
     387          69 : CPLXMLNode *PDS4TableBaseLayer::RefreshFileAreaObservationalBeginningCommon(
     388             :     CPLXMLNode *psFAO, const CPLString &osPrefix, const char *pszTableEltName,
     389             :     CPLString &osDescription)
     390             : {
     391          69 :     CPLXMLNode *psFile = CPLGetXMLNode(psFAO, (osPrefix + "File").c_str());
     392          69 :     CPLAssert(psFile);
     393             :     CPLXMLNode *psfile_size =
     394          69 :         CPLGetXMLNode(psFile, (osPrefix + "file_size").c_str());
     395          69 :     if (psfile_size)
     396             :     {
     397           3 :         CPLRemoveXMLChild(psFile, psfile_size);
     398           3 :         CPLDestroyXMLNode(psfile_size);
     399             :     }
     400             : 
     401          69 :     CPLXMLNode *psHeader = CPLGetXMLNode(psFAO, (osPrefix + "Header").c_str());
     402          69 :     if (psHeader)
     403             :     {
     404           3 :         CPLRemoveXMLChild(psFAO, psHeader);
     405           3 :         CPLDestroyXMLNode(psHeader);
     406             :     }
     407             : 
     408         138 :     CPLString osTableEltName(osPrefix + pszTableEltName);
     409          69 :     CPLXMLNode *psTable = CPLGetXMLNode(psFAO, osTableEltName);
     410         138 :     CPLString osName;
     411          69 :     CPLString osLocalIdentifier;
     412          69 :     if (psTable)
     413             :     {
     414           4 :         osName = CPLGetXMLValue(psTable, (osPrefix + "name").c_str(), "");
     415             :         osLocalIdentifier = CPLGetXMLValue(
     416           4 :             psTable, (osPrefix + "local_identifier").c_str(), "");
     417             :         osDescription =
     418           4 :             CPLGetXMLValue(psTable, (osPrefix + "description").c_str(), "");
     419           4 :         CPLRemoveXMLChild(psFAO, psTable);
     420           4 :         CPLDestroyXMLNode(psTable);
     421             :     }
     422             : 
     423             :     // Write Table_Delimited/Table_Character/Table_Binary
     424          69 :     psTable = CPLCreateXMLNode(psFAO, CXT_Element, osTableEltName);
     425          69 :     if (!osName.empty())
     426           3 :         CPLCreateXMLElementAndValue(psTable, (osPrefix + "name").c_str(),
     427             :                                     osName);
     428          69 :     if (osLocalIdentifier.empty())
     429             :     {
     430             :         // Make a valid NCName
     431          69 :         osLocalIdentifier = GetName();
     432          69 :         if (isdigit(static_cast<unsigned char>(osLocalIdentifier[0])))
     433             :         {
     434           4 :             osLocalIdentifier = '_' + osLocalIdentifier;
     435             :         }
     436         760 :         for (char &ch : osLocalIdentifier)
     437             :         {
     438         691 :             if (!isalnum(static_cast<unsigned char>(ch)) &&
     439          76 :                 static_cast<unsigned>(ch) <= 127)
     440          76 :                 ch = '_';
     441             :         }
     442             :     }
     443         138 :     CPLCreateXMLElementAndValue(
     444         138 :         psTable, (osPrefix + "local_identifier").c_str(), osLocalIdentifier);
     445             : 
     446             :     CPLXMLNode *psOffset =
     447          69 :         CPLCreateXMLElementAndValue(psTable, (osPrefix + "offset").c_str(),
     448             :                                     CPLSPrintf(CPL_FRMT_GUIB, m_nOffset));
     449          69 :     CPLAddXMLAttributeAndValue(psOffset, "unit", "byte");
     450             : 
     451         138 :     return psTable;
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                       ParseLineEndingOption()                        */
     456             : /************************************************************************/
     457             : 
     458          63 : void PDS4TableBaseLayer::ParseLineEndingOption(CSLConstList papszOptions)
     459             : {
     460             :     const char *pszLineEnding =
     461          63 :         CSLFetchNameValueDef(papszOptions, "LINE_ENDING", "CRLF");
     462          63 :     if (EQUAL(pszLineEnding, "CRLF"))
     463             :     {
     464          59 :         m_osLineEnding = "\r\n";
     465             :     }
     466           4 :     else if (EQUAL(pszLineEnding, "LF"))
     467             :     {
     468           2 :         m_osLineEnding = "\n";
     469             :     }
     470             :     else
     471             :     {
     472           2 :         m_osLineEnding = "\r\n";
     473           2 :         CPLError(CE_Warning, CPLE_AppDefined,
     474             :                  "Unhandled value for LINE_ENDING");
     475             :     }
     476          63 : }
     477             : 
     478             : /************************************************************************/
     479             : /*                             GetDataset()                             */
     480             : /************************************************************************/
     481             : 
     482          17 : GDALDataset *PDS4TableBaseLayer::GetDataset()
     483             : {
     484          17 :     return m_poDS;
     485             : }
     486             : 
     487             : /************************************************************************/
     488             : /* ==================================================================== */
     489             : /*                        PDS4FixedWidthTable                           */
     490             : /* ==================================================================== */
     491             : /************************************************************************/
     492             : 
     493          46 : PDS4FixedWidthTable::PDS4FixedWidthTable(PDS4Dataset *poDS, const char *pszName,
     494          46 :                                          const char *pszFilename, bool bUpdate)
     495          46 :     : PDS4TableBaseLayer(poDS, pszName, pszFilename, bUpdate)
     496             : {
     497          46 : }
     498             : 
     499             : /************************************************************************/
     500             : /*                            ResetReading()                            */
     501             : /************************************************************************/
     502             : 
     503         215 : void PDS4FixedWidthTable::ResetReading()
     504             : {
     505         215 :     m_nFID = 1;
     506         215 : }
     507             : 
     508             : /************************************************************************/
     509             : /*                           GetNextFeature()                           */
     510             : /************************************************************************/
     511             : 
     512         470 : OGRFeature *PDS4FixedWidthTable::GetNextFeature()
     513             : {
     514             :     while (true)
     515             :     {
     516         470 :         auto poFeature = GetFeature(m_nFID);
     517         470 :         if (poFeature == nullptr)
     518             :         {
     519          77 :             return nullptr;
     520             :         }
     521         393 :         ++m_nFID;
     522             : 
     523         896 :         if ((m_poFilterGeom == nullptr ||
     524         746 :              FilterGeometry(poFeature->GetGeometryRef())) &&
     525         353 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     526             :         {
     527         318 :             return poFeature;
     528             :         }
     529          75 :         delete poFeature;
     530          75 :     }
     531             : }
     532             : 
     533             : /************************************************************************/
     534             : /*                           TestCapability()                           */
     535             : /************************************************************************/
     536             : 
     537         191 : bool PDS4FixedWidthTable::TestCapability(const char *pszCap) const
     538             : {
     539         191 :     if (EQUAL(pszCap, OLCRandomRead) || EQUAL(pszCap, OLCStringsAsUTF8) ||
     540         165 :         EQUAL(pszCap, OLCZGeometries))
     541             :     {
     542          32 :         return true;
     543             :     }
     544         159 :     if (EQUAL(pszCap, OLCFastFeatureCount))
     545             :     {
     546           0 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
     547             :     }
     548         159 :     if (EQUAL(pszCap, OLCCreateField))
     549             :     {
     550          79 :         return m_poDS->GetAccess() == GA_Update && m_nFeatureCount == 0;
     551             :     }
     552          80 :     if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite))
     553             :     {
     554          14 :         return m_poDS->GetAccess() == GA_Update;
     555             :     }
     556          66 :     return false;
     557             : }
     558             : 
     559             : /************************************************************************/
     560             : /*                            ISetFeature()                             */
     561             : /************************************************************************/
     562             : 
     563          23 : OGRErr PDS4FixedWidthTable::ISetFeature(OGRFeature *poFeature)
     564             : {
     565          23 :     if (poFeature->GetFID() <= 0 || poFeature->GetFID() > m_nFeatureCount)
     566             :     {
     567           1 :         return OGRERR_NON_EXISTING_FEATURE;
     568             :     }
     569          22 :     if (m_poDS->GetAccess() != GA_Update)
     570             :     {
     571           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     572             :                  "Dataset opened in read-only mode");
     573           0 :         return OGRERR_FAILURE;
     574             :     }
     575          22 :     CPLAssert(static_cast<int>(m_osBuffer.size()) == m_nRecordSize);
     576          22 :     CPLAssert(m_nRecordSize > static_cast<int>(m_osLineEnding.size()));
     577             : 
     578          22 :     VSIFSeekL(m_fp, m_nOffset + (poFeature->GetFID() - 1) * m_nRecordSize,
     579             :               SEEK_SET);
     580          22 :     memset(&m_osBuffer[0], ' ', m_nRecordSize);
     581             : 
     582          22 :     OGRFeature *poRawFeature = AddFieldsFromGeometry(poFeature);
     583         347 :     for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
     584             :     {
     585         325 :         if (!poRawFeature->IsFieldSetAndNotNull(i))
     586             :         {
     587          21 :             continue;
     588             :         }
     589         608 :         CPLString osBuffer;
     590         304 :         const CPLString &osDT(m_aoFields[i].m_osDataType);
     591         304 :         const auto eType(m_poRawFeatureDefn->GetFieldDefn(i)->GetType());
     592         304 :         if (osDT == "ASCII_Real")
     593             :         {
     594         456 :             CPLString osFormat;
     595         228 :             osFormat.Printf("%%.%dg", m_aoFields[i].m_nLength - 2);
     596             :             osBuffer.Printf(osFormat.c_str(),
     597         228 :                             poRawFeature->GetFieldAsDouble(i));
     598             :         }
     599         143 :         else if (osDT == "ASCII_Integer" ||
     600         143 :                  osDT == "ASCII_NonNegative_Integer" || eType == OFTString)
     601             :         {
     602          17 :             osBuffer = poRawFeature->GetFieldAsString(i);
     603             :         }
     604          59 :         else if (osDT == "ASCII_Boolean")
     605             :         {
     606           8 :             osBuffer = poRawFeature->GetFieldAsInteger(i) == 1 ? "1" : "0";
     607             :         }
     608          51 :         else if (osDT == "IEEE754LSBDouble")
     609             :         {
     610           5 :             double dfVal = poRawFeature->GetFieldAsDouble(i);
     611           5 :             CPL_LSBPTR64(&dfVal);
     612           5 :             osBuffer.resize(sizeof(dfVal));
     613           5 :             memcpy(&osBuffer[0], &dfVal, sizeof(dfVal));
     614             :         }
     615          46 :         else if (osDT == "IEEE754MSBDouble")
     616             :         {
     617           2 :             double dfVal = poRawFeature->GetFieldAsDouble(i);
     618           2 :             CPL_MSBPTR64(&dfVal);
     619           2 :             osBuffer.resize(sizeof(dfVal));
     620           2 :             memcpy(&osBuffer[0], &dfVal, sizeof(dfVal));
     621             :         }
     622          44 :         else if (osDT == "IEEE754LSBSingle")
     623             :         {
     624           2 :             float fVal = static_cast<float>(poRawFeature->GetFieldAsDouble(i));
     625           2 :             CPL_LSBPTR32(&fVal);
     626           2 :             osBuffer.resize(sizeof(fVal));
     627           2 :             memcpy(&osBuffer[0], &fVal, sizeof(fVal));
     628             :         }
     629          42 :         else if (osDT == "IEEE754MSBSingle")
     630             :         {
     631           2 :             float fVal = static_cast<float>(poRawFeature->GetFieldAsDouble(i));
     632           2 :             CPL_MSBPTR32(&fVal);
     633           2 :             osBuffer.resize(sizeof(fVal));
     634           2 :             memcpy(&osBuffer[0], &fVal, sizeof(fVal));
     635             :         }
     636          40 :         else if (osDT == "SignedByte")
     637             :         {
     638           2 :             signed char bVal = static_cast<signed char>(std::max(
     639           2 :                 -128, std::min(127, poRawFeature->GetFieldAsInteger(i))));
     640           2 :             osBuffer.resize(sizeof(bVal));
     641           2 :             memcpy(&osBuffer[0], &bVal, sizeof(bVal));
     642             :         }
     643          38 :         else if (osDT == "UnsignedByte")
     644             :         {
     645             :             GByte ubVal = static_cast<GByte>(
     646           2 :                 std::max(0, std::min(255, poRawFeature->GetFieldAsInteger(i))));
     647           2 :             osBuffer.resize(sizeof(ubVal));
     648           2 :             memcpy(&osBuffer[0], &ubVal, sizeof(ubVal));
     649             :         }
     650          36 :         else if (osDT == "SignedLSB2")
     651             :         {
     652           1 :             GInt16 sVal = static_cast<GInt16>(std::max(
     653           1 :                 -32768, std::min(32767, poRawFeature->GetFieldAsInteger(i))));
     654           1 :             CPL_LSBPTR16(&sVal);
     655           1 :             osBuffer.resize(sizeof(sVal));
     656           1 :             memcpy(&osBuffer[0], &sVal, sizeof(sVal));
     657             :         }
     658          35 :         else if (osDT == "SignedMSB2")
     659             :         {
     660           1 :             GInt16 sVal = static_cast<GInt16>(std::max(
     661           1 :                 -32768, std::min(32767, poRawFeature->GetFieldAsInteger(i))));
     662           1 :             CPL_MSBPTR16(&sVal);
     663           1 :             osBuffer.resize(sizeof(sVal));
     664           1 :             memcpy(&osBuffer[0], &sVal, sizeof(sVal));
     665             :         }
     666          34 :         else if (osDT == "UnsignedLSB2")
     667             :         {
     668           1 :             GUInt16 usVal = static_cast<GUInt16>(std::max(
     669           1 :                 0, std::min(65535, poRawFeature->GetFieldAsInteger(i))));
     670           1 :             CPL_LSBPTR16(&usVal);
     671           1 :             osBuffer.resize(sizeof(usVal));
     672           1 :             memcpy(&osBuffer[0], &usVal, sizeof(usVal));
     673             :         }
     674          33 :         else if (osDT == "UnsignedMSB2")
     675             :         {
     676           1 :             GUInt16 usVal = static_cast<GUInt16>(std::max(
     677           1 :                 0, std::min(65535, poRawFeature->GetFieldAsInteger(i))));
     678           1 :             CPL_MSBPTR16(&usVal);
     679           1 :             osBuffer.resize(sizeof(usVal));
     680           1 :             memcpy(&osBuffer[0], &usVal, sizeof(usVal));
     681             :         }
     682          32 :         else if (osDT == "SignedLSB4")
     683             :         {
     684           1 :             GInt32 nVal = poRawFeature->GetFieldAsInteger(i);
     685           1 :             CPL_LSBPTR32(&nVal);
     686           1 :             osBuffer.resize(sizeof(nVal));
     687           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     688             :         }
     689          31 :         else if (osDT == "SignedMSB4")
     690             :         {
     691           1 :             GInt32 nVal = poRawFeature->GetFieldAsInteger(i);
     692           1 :             CPL_MSBPTR32(&nVal);
     693           1 :             osBuffer.resize(sizeof(nVal));
     694           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     695             :         }
     696          30 :         else if (osDT == "UnsignedLSB4")
     697             :         {
     698           1 :             GUInt32 nVal = static_cast<GUInt32>(
     699           1 :                 std::max(0, poRawFeature->GetFieldAsInteger(i)));
     700           1 :             CPL_LSBPTR32(&nVal);
     701           1 :             osBuffer.resize(sizeof(nVal));
     702           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     703             :         }
     704          29 :         else if (osDT == "UnsignedMSB4")
     705             :         {
     706           1 :             GUInt32 nVal = static_cast<GUInt32>(
     707           1 :                 std::max(0, poRawFeature->GetFieldAsInteger(i)));
     708           1 :             CPL_MSBPTR32(&nVal);
     709           1 :             osBuffer.resize(sizeof(nVal));
     710           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     711             :         }
     712          28 :         else if (osDT == "SignedLSB8")
     713             :         {
     714           1 :             GInt64 nVal = poRawFeature->GetFieldAsInteger64(i);
     715           1 :             CPL_LSBPTR64(&nVal);
     716           1 :             osBuffer.resize(sizeof(nVal));
     717           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     718             :         }
     719          27 :         else if (osDT == "SignedMSB8")
     720             :         {
     721           1 :             GInt64 nVal = poRawFeature->GetFieldAsInteger64(i);
     722           1 :             CPL_MSBPTR64(&nVal);
     723           1 :             osBuffer.resize(sizeof(nVal));
     724           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     725             :         }
     726          26 :         else if (osDT == "UnsignedLSB8")
     727             :         {
     728           1 :             GUInt64 nVal = static_cast<GUInt64>(std::max(
     729           1 :                 static_cast<GIntBig>(0), poRawFeature->GetFieldAsInteger64(i)));
     730           1 :             CPL_LSBPTR64(&nVal);
     731           1 :             osBuffer.resize(sizeof(nVal));
     732           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     733             :         }
     734          25 :         else if (osDT == "UnsignedMSB8")
     735             :         {
     736           1 :             GUInt64 nVal = static_cast<GUInt64>(std::max(
     737           1 :                 static_cast<GIntBig>(0), poRawFeature->GetFieldAsInteger64(i)));
     738           1 :             CPL_MSBPTR64(&nVal);
     739           1 :             osBuffer.resize(sizeof(nVal));
     740           1 :             memcpy(&osBuffer[0], &nVal, sizeof(nVal));
     741             :         }
     742          40 :         else if (osDT == "ASCII_Date_Time_YMD" ||
     743          16 :                  osDT == "ASCII_Date_Time_YMD_UTC")
     744             :         {
     745             :             char *pszDateTime =
     746           8 :                 OGRGetXMLDateTime(poRawFeature->GetRawFieldRef(i));
     747           8 :             osBuffer = pszDateTime;
     748           8 :             CPLFree(pszDateTime);
     749             :         }
     750          16 :         else if (osDT == "ASCII_Date_YMD")
     751             :         {
     752             :             int nYear, nMonth, nDay;
     753           8 :             poRawFeature->GetFieldAsDateTime(
     754             :                 i, &nYear, &nMonth, &nDay, nullptr, nullptr,
     755             :                 static_cast<float *>(nullptr), nullptr);
     756           8 :             osBuffer.Printf("%04d-%02d-%02d", nYear, nMonth, nDay);
     757             :         }
     758           8 :         else if (osDT == "ASCII_Time")
     759             :         {
     760             :             int nHour, nMin;
     761             :             float fSec;
     762           8 :             poRawFeature->GetFieldAsDateTime(i, nullptr, nullptr, nullptr,
     763             :                                              &nHour, &nMin, &fSec, nullptr);
     764           8 :             osBuffer.Printf("%02d:%02d:%05.3f", nHour, nMin, fSec);
     765             :         }
     766             : 
     767         608 :         if (!osBuffer.empty() &&
     768         304 :             osBuffer.size() <= static_cast<size_t>(m_aoFields[i].m_nLength))
     769             :         {
     770         304 :             memcpy(&m_osBuffer[m_aoFields[i].m_nOffset +
     771         304 :                                m_aoFields[i].m_nLength - osBuffer.size()],
     772         304 :                    &osBuffer[0], osBuffer.size());
     773             :         }
     774           0 :         else if (!osBuffer.empty())
     775             :         {
     776           0 :             if (eType == OFTString)
     777             :             {
     778           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     779             :                          "Value %s for field %s is too large. Truncating it",
     780             :                          osBuffer.c_str(),
     781           0 :                          m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef());
     782           0 :                 memcpy(&m_osBuffer[m_aoFields[i].m_nOffset], osBuffer.data(),
     783           0 :                        m_aoFields[i].m_nLength);
     784             :             }
     785             :             else
     786             :             {
     787           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     788             :                          "Value %s for field %s is too large. Omitting i",
     789             :                          osBuffer.c_str(),
     790           0 :                          m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef());
     791             :             }
     792             :         }
     793             :     }
     794          22 :     delete poRawFeature;
     795             : 
     796          22 :     if (!m_osLineEnding.empty())
     797             :     {
     798          34 :         memcpy(&m_osBuffer[m_osBuffer.size() - m_osLineEnding.size()],
     799          17 :                m_osLineEnding.data(), m_osLineEnding.size());
     800             :     }
     801             : 
     802          22 :     if (VSIFWriteL(&m_osBuffer[0], m_nRecordSize, 1, m_fp) != 1)
     803             :     {
     804           0 :         return OGRERR_FAILURE;
     805             :     }
     806             : 
     807          22 :     return OGRERR_NONE;
     808             : }
     809             : 
     810             : /************************************************************************/
     811             : /*                           ICreateFeature()                           */
     812             : /************************************************************************/
     813             : 
     814          22 : OGRErr PDS4FixedWidthTable::ICreateFeature(OGRFeature *poFeature)
     815             : {
     816          22 :     if (!m_bUpdate)
     817             :     {
     818           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     819             :                  "Operation not supported on read-only layer");
     820           0 :         return OGRERR_FAILURE;
     821             :     }
     822             : 
     823          22 :     m_nFeatureCount++;
     824          22 :     poFeature->SetFID(m_nFeatureCount);
     825          22 :     OGRErr eErr = ISetFeature(poFeature);
     826          22 :     if (eErr == OGRERR_NONE)
     827             :     {
     828          22 :         MarkHeaderDirty();
     829             :     }
     830             :     else
     831             :     {
     832           0 :         poFeature->SetFID(-1);
     833           0 :         m_nFeatureCount--;
     834             :     }
     835          22 :     return eErr;
     836             : }
     837             : 
     838             : /************************************************************************/
     839             : /*                             GetFeature()                             */
     840             : /************************************************************************/
     841             : 
     842         497 : OGRFeature *PDS4FixedWidthTable::GetFeature(GIntBig nFID)
     843             : {
     844         497 :     if (nFID <= 0 || nFID > m_nFeatureCount)
     845             :     {
     846          87 :         return nullptr;
     847             :     }
     848         410 :     VSIFSeekL(m_fp, m_nOffset + (nFID - 1) * m_nRecordSize, SEEK_SET);
     849         410 :     if (VSIFReadL(&m_osBuffer[0], m_nRecordSize, 1, m_fp) != 1)
     850             :     {
     851           0 :         return nullptr;
     852             :     }
     853         410 :     OGRFeature *poRawFeature = new OGRFeature(m_poRawFeatureDefn);
     854         410 :     poRawFeature->SetFID(nFID);
     855        9448 :     for (int i = 0; i < poRawFeature->GetFieldCount(); i++)
     856             :     {
     857        9038 :         CPLString osVal(m_osBuffer.substr(m_aoFields[i].m_nOffset,
     858       18076 :                                           m_aoFields[i].m_nLength));
     859             : 
     860        9038 :         const CPLString &osDT(m_aoFields[i].m_osDataType);
     861        9038 :         if (STARTS_WITH(osDT, "ASCII_") || STARTS_WITH(osDT, "UTF8_"))
     862             :         {
     863        8779 :             osVal.Trim();
     864        8779 :             if (osVal.empty())
     865             :             {
     866          42 :                 continue;
     867             :             }
     868             :         }
     869             : 
     870        8996 :         if (osDT == "IEEE754LSBDouble")
     871             :         {
     872             :             double dfVal;
     873           5 :             CPLAssert(osVal.size() == sizeof(dfVal));
     874           5 :             memcpy(&dfVal, osVal.data(), sizeof(dfVal));
     875           5 :             CPL_LSBPTR64(&dfVal);
     876           5 :             poRawFeature->SetField(i, dfVal);
     877             :         }
     878        8991 :         else if (osDT == "IEEE754MSBDouble")
     879             :         {
     880             :             double dfVal;
     881           2 :             CPLAssert(osVal.size() == sizeof(dfVal));
     882           2 :             memcpy(&dfVal, osVal.data(), sizeof(dfVal));
     883           2 :             CPL_MSBPTR64(&dfVal);
     884           2 :             poRawFeature->SetField(i, dfVal);
     885             :         }
     886        8989 :         else if (osDT == "IEEE754LSBSingle")
     887             :         {
     888             :             float fVal;
     889           2 :             CPLAssert(osVal.size() == sizeof(fVal));
     890           2 :             memcpy(&fVal, osVal.data(), sizeof(fVal));
     891           2 :             CPL_LSBPTR32(&fVal);
     892           2 :             poRawFeature->SetField(i, fVal);
     893             :         }
     894        8987 :         else if (osDT == "IEEE754MSBSingle")
     895             :         {
     896             :             float fVal;
     897           2 :             CPLAssert(osVal.size() == sizeof(fVal));
     898           2 :             memcpy(&fVal, osVal.data(), sizeof(fVal));
     899           2 :             CPL_MSBPTR32(&fVal);
     900           2 :             poRawFeature->SetField(i, fVal);
     901             :         }
     902        8985 :         else if (osDT == "SignedByte")
     903             :         {
     904             :             signed char bVal;
     905           2 :             CPLAssert(osVal.size() == sizeof(bVal));
     906           2 :             memcpy(&bVal, osVal.data(), sizeof(bVal));
     907           2 :             poRawFeature->SetField(i, bVal);
     908             :         }
     909        8983 :         else if (osDT == "UnsignedByte")
     910             :         {
     911             :             GByte bVal;
     912           2 :             CPLAssert(osVal.size() == sizeof(bVal));
     913           2 :             memcpy(&bVal, osVal.data(), sizeof(bVal));
     914           2 :             poRawFeature->SetField(i, bVal);
     915             :         }
     916        8981 :         else if (osDT == "SignedLSB2")
     917             :         {
     918             :             GInt16 sVal;
     919           1 :             CPLAssert(osVal.size() == sizeof(sVal));
     920           1 :             memcpy(&sVal, osVal.data(), sizeof(sVal));
     921           1 :             CPL_LSBPTR16(&sVal);
     922           1 :             poRawFeature->SetField(i, sVal);
     923             :         }
     924        8980 :         else if (osDT == "SignedMSB2")
     925             :         {
     926             :             GInt16 sVal;
     927           1 :             CPLAssert(osVal.size() == sizeof(sVal));
     928           1 :             memcpy(&sVal, osVal.data(), sizeof(sVal));
     929           1 :             CPL_MSBPTR16(&sVal);
     930           1 :             poRawFeature->SetField(i, sVal);
     931             :         }
     932        8979 :         else if (osDT == "UnsignedLSB2")
     933             :         {
     934             :             GUInt16 usVal;
     935           1 :             CPLAssert(osVal.size() == sizeof(usVal));
     936           1 :             memcpy(&usVal, osVal.data(), sizeof(usVal));
     937           1 :             CPL_LSBPTR16(&usVal);
     938           1 :             poRawFeature->SetField(i, usVal);
     939             :         }
     940        8978 :         else if (osDT == "UnsignedMSB2")
     941             :         {
     942             :             GUInt16 usVal;
     943         232 :             CPLAssert(osVal.size() == sizeof(usVal));
     944         232 :             memcpy(&usVal, osVal.data(), sizeof(usVal));
     945         232 :             CPL_MSBPTR16(&usVal);
     946         232 :             poRawFeature->SetField(i, usVal);
     947             :         }
     948        8746 :         else if (osDT == "SignedLSB4")
     949             :         {
     950             :             GInt32 nVal;
     951           1 :             CPLAssert(osVal.size() == sizeof(nVal));
     952           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
     953           1 :             CPL_LSBPTR32(&nVal);
     954           1 :             poRawFeature->SetField(i, nVal);
     955             :         }
     956        8745 :         else if (osDT == "SignedMSB4")
     957             :         {
     958             :             GInt32 nVal;
     959           1 :             CPLAssert(osVal.size() == sizeof(nVal));
     960           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
     961           1 :             CPL_MSBPTR32(&nVal);
     962           1 :             poRawFeature->SetField(i, nVal);
     963             :         }
     964        8744 :         else if (osDT == "UnsignedLSB4")
     965             :         {
     966             :             GUInt32 nVal;
     967           1 :             CPLAssert(osVal.size() == sizeof(nVal));
     968           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
     969           1 :             CPL_LSBPTR32(&nVal);
     970           1 :             poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
     971             :         }
     972        8743 :         else if (osDT == "UnsignedMSB4")
     973             :         {
     974             :             GUInt32 nVal;
     975           2 :             CPLAssert(osVal.size() == sizeof(nVal));
     976           2 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
     977           2 :             CPL_MSBPTR32(&nVal);
     978           2 :             poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
     979             :         }
     980        8741 :         else if (osDT == "SignedLSB8")
     981             :         {
     982             :             GInt64 nVal;
     983           1 :             CPLAssert(osVal.size() == sizeof(nVal));
     984           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
     985           1 :             CPL_LSBPTR64(&nVal);
     986           1 :             poRawFeature->SetField(i, nVal);
     987             :         }
     988        8740 :         else if (osDT == "SignedMSB8")
     989             :         {
     990             :             GInt64 nVal;
     991           1 :             CPLAssert(osVal.size() == sizeof(nVal));
     992           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
     993           1 :             CPL_MSBPTR64(&nVal);
     994           1 :             poRawFeature->SetField(i, nVal);
     995             :         }
     996        8739 :         else if (osDT == "UnsignedLSB8")
     997             :         {
     998             :             GUInt64 nVal;
     999           1 :             CPLAssert(osVal.size() == sizeof(nVal));
    1000           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
    1001           1 :             CPL_LSBPTR64(&nVal);
    1002           1 :             poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
    1003             :         }
    1004        8738 :         else if (osDT == "UnsignedMSB8")
    1005             :         {
    1006             :             GUInt64 nVal;
    1007           1 :             CPLAssert(osVal.size() == sizeof(nVal));
    1008           1 :             memcpy(&nVal, osVal.data(), sizeof(nVal));
    1009           1 :             CPL_MSBPTR64(&nVal);
    1010           1 :             poRawFeature->SetField(i, static_cast<GIntBig>(nVal));
    1011             :         }
    1012        8737 :         else if (osDT == "ASCII_Boolean")
    1013             :         {
    1014           9 :             poRawFeature->SetField(
    1015           9 :                 i, EQUAL(osVal, "t") || EQUAL(osVal, "1") ? 1 : 0);
    1016             :         }
    1017             :         else
    1018             :         {
    1019        8728 :             poRawFeature->SetField(i, osVal.c_str());
    1020             :         }
    1021             :     }
    1022         410 :     OGRFeature *poFeature = AddGeometryFromFields(poRawFeature);
    1023         410 :     delete poRawFeature;
    1024         410 :     return poFeature;
    1025             : }
    1026             : 
    1027             : /************************************************************************/
    1028             : /*                    GetFieldTypeFromPDS4DataType()                    */
    1029             : /************************************************************************/
    1030             : 
    1031         851 : static OGRFieldType GetFieldTypeFromPDS4DataType(const char *pszDataType,
    1032             :                                                  int nDTSize,
    1033             :                                                  OGRFieldSubType &eSubType,
    1034             :                                                  bool &error)
    1035             : {
    1036         851 :     OGRFieldType eType = OFTString;
    1037         851 :     eSubType = OFSTNone;
    1038         851 :     error = false;
    1039         851 :     if (EQUAL(pszDataType, "ASCII_Boolean"))
    1040             :     {
    1041          29 :         eSubType = OFSTBoolean;
    1042          29 :         eType = OFTInteger;
    1043             :     }
    1044         822 :     else if (EQUAL(pszDataType, "ASCII_Date_Time_YMD") ||
    1045         777 :              EQUAL(pszDataType, "ASCII_Date_Time_YMD_UTC"))
    1046             :     {
    1047          45 :         eType = OFTDateTime;
    1048             :     }
    1049         777 :     else if (EQUAL(pszDataType, "ASCII_Date_YMD"))
    1050             :     {
    1051          45 :         eType = OFTDate;
    1052             :     }
    1053         732 :     else if (EQUAL(pszDataType, "ASCII_Integer") ||
    1054         668 :              EQUAL(pszDataType, "ASCII_NonNegative_Integer"))
    1055             :     {
    1056          64 :         eType = OFTInteger;
    1057             :     }
    1058         668 :     else if (EQUAL(pszDataType, "SignedByte") ||
    1059         664 :              EQUAL(pszDataType, "UnsignedByte"))
    1060             :     {
    1061           9 :         if (nDTSize != 1)
    1062           0 :             error = true;
    1063           9 :         eType = OFTInteger;
    1064             :     }
    1065         659 :     else if (EQUAL(pszDataType, "SignedLSB2") ||
    1066         657 :              EQUAL(pszDataType, "SignedMSB2"))
    1067             :     {
    1068           4 :         if (nDTSize != 2)
    1069           0 :             error = true;
    1070           4 :         eType = OFTInteger;
    1071           4 :         eSubType = OFSTInt16;
    1072             :     }
    1073         655 :     else if (EQUAL(pszDataType, "UnsignedLSB2") ||
    1074         653 :              EQUAL(pszDataType, "UnsignedMSB2"))
    1075             :     {
    1076         236 :         if (nDTSize != 2)
    1077           0 :             error = true;
    1078         236 :         eType = OFTInteger;
    1079             :     }
    1080         419 :     else if (EQUAL(pszDataType, "SignedLSB4") ||
    1081         417 :              EQUAL(pszDataType, "SignedMSB4"))
    1082             :     {
    1083           4 :         if (nDTSize != 4)
    1084           0 :             error = true;
    1085           4 :         eType = OFTInteger;
    1086             :     }
    1087         415 :     else if (EQUAL(pszDataType, "UnsignedLSB4") ||
    1088         413 :              EQUAL(pszDataType, "UnsignedMSB4"))
    1089             :     {
    1090           6 :         if (nDTSize != 4)
    1091           0 :             error = true;
    1092             :         // Use larger data type as > 2 billion values don't hold on signed int32
    1093           6 :         eType = OFTInteger64;
    1094             :     }
    1095         409 :     else if (EQUAL(pszDataType, "SignedLSB8") ||
    1096         407 :              EQUAL(pszDataType, "SignedMSB8"))
    1097             :     {
    1098           4 :         if (nDTSize != 8)
    1099           0 :             error = true;
    1100           4 :         eType = OFTInteger64;
    1101             :     }
    1102         405 :     else if (EQUAL(pszDataType, "UnsignedLSB8") ||
    1103         403 :              EQUAL(pszDataType, "UnsignedMSB8"))
    1104             :     {
    1105           5 :         if (nDTSize != 8)
    1106           0 :             error = true;
    1107             :         // Hope that we won't get value larger than > 2^63...
    1108           5 :         eType = OFTInteger64;
    1109             :     }
    1110         400 :     else if (EQUAL(pszDataType, "ASCII_Real"))
    1111             :     {
    1112         240 :         eType = OFTReal;
    1113             :     }
    1114         160 :     else if (EQUAL(pszDataType, "IEEE754LSBDouble") ||
    1115         153 :              EQUAL(pszDataType, "IEEE754MSBDouble"))
    1116             :     {
    1117          12 :         if (nDTSize != 8)
    1118           0 :             error = true;
    1119          12 :         eType = OFTReal;
    1120             :     }
    1121         148 :     else if (EQUAL(pszDataType, "IEEE754LSBSingle") ||
    1122         144 :              EQUAL(pszDataType, "IEEE754MSBSingle"))
    1123             :     {
    1124           9 :         if (nDTSize != 4)
    1125           0 :             error = true;
    1126           9 :         eType = OFTReal;
    1127           9 :         eSubType = OFSTFloat32;
    1128             :     }
    1129         139 :     else if (EQUAL(pszDataType, "ASCII_Time"))
    1130             :     {
    1131          29 :         eType = OFTTime;
    1132             :     }
    1133         851 :     return eType;
    1134             : }
    1135             : 
    1136             : /************************************************************************/
    1137             : /*                            ReadTableDef()                            */
    1138             : /************************************************************************/
    1139             : 
    1140          33 : bool PDS4FixedWidthTable::ReadTableDef(const CPLXMLNode *psTable)
    1141             : {
    1142          33 :     CPLAssert(m_fp == nullptr);
    1143          33 :     m_fp = VSIFOpenL(m_osFilename,
    1144          33 :                      (m_poDS->GetAccess() == GA_ReadOnly) ? "rb" : "r+b");
    1145          33 :     if (!m_fp)
    1146             :     {
    1147           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
    1148             :                  m_osFilename.c_str());
    1149           0 :         return false;
    1150             :     }
    1151             : 
    1152          33 :     m_nOffset = static_cast<GUIntBig>(
    1153          33 :         CPLAtoGIntBig(CPLGetXMLValue(psTable, "offset", "0")));
    1154             : 
    1155          33 :     m_nFeatureCount = CPLAtoGIntBig(CPLGetXMLValue(psTable, "records", "-1"));
    1156             : 
    1157             :     const char *pszRecordDelimiter =
    1158          33 :         CPLGetXMLValue(psTable, "record_delimiter", "");
    1159          33 :     if (EQUAL(pszRecordDelimiter, "Carriage-Return Line-Feed"))
    1160          20 :         m_osLineEnding = "\r\n";
    1161          13 :     else if (EQUAL(pszRecordDelimiter, "Line-Feed"))
    1162           2 :         m_osLineEnding = "\n";
    1163          11 :     else if (EQUAL(pszRecordDelimiter, ""))
    1164             :     {
    1165          11 :         if (GetSubType() == "Character")
    1166             :         {
    1167           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing record_delimiter");
    1168           0 :             return false;
    1169             :         }
    1170             :     }
    1171             :     else
    1172             :     {
    1173           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid record_delimiter");
    1174           0 :         return false;
    1175             :     }
    1176             : 
    1177             :     const CPLXMLNode *psRecord =
    1178          33 :         CPLGetXMLNode(psTable, ("Record_" + GetSubType()).c_str());
    1179          33 :     if (!psRecord)
    1180             :     {
    1181           0 :         return false;
    1182             :     }
    1183          33 :     m_nRecordSize = atoi(CPLGetXMLValue(psRecord, "record_length", "0"));
    1184          66 :     if (m_nRecordSize <= static_cast<int>(m_osLineEnding.size()) ||
    1185          33 :         m_nRecordSize > 1000 * 1000)
    1186             :     {
    1187           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid record_length");
    1188           0 :         return false;
    1189             :     }
    1190          33 :     m_osBuffer.resize(m_nRecordSize);
    1191          33 :     if (!ReadFields(psRecord, 0, ""))
    1192             :     {
    1193           0 :         return false;
    1194             :     }
    1195             : 
    1196          33 :     SetupGeomField();
    1197             : 
    1198          33 :     return true;
    1199             : }
    1200             : 
    1201             : /************************************************************************/
    1202             : /*                             ReadFields()                             */
    1203             : /************************************************************************/
    1204             : 
    1205         264 : bool PDS4FixedWidthTable::ReadFields(const CPLXMLNode *psParent,
    1206             :                                      int nBaseOffset,
    1207             :                                      const CPLString &osSuffixFieldName)
    1208             : {
    1209        2367 :     for (const CPLXMLNode *psIter = psParent->psChild; psIter;
    1210        2103 :          psIter = psIter->psNext)
    1211             :     {
    1212        4206 :         if (psIter->eType == CXT_Element &&
    1213        4206 :             strcmp(psIter->pszValue, ("Field_" + GetSubType()).c_str()) == 0)
    1214             :         {
    1215         617 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    1216         617 :             if (!pszName)
    1217             :             {
    1218           0 :                 return false;
    1219             :             }
    1220             :             const char *pszLoc =
    1221         617 :                 CPLGetXMLValue(psIter, "field_location", nullptr);
    1222         617 :             if (!pszLoc)
    1223             :             {
    1224           0 :                 return false;
    1225             :             }
    1226             :             const char *pszDataType =
    1227         617 :                 CPLGetXMLValue(psIter, "data_type", nullptr);
    1228         617 :             if (!pszDataType)
    1229             :             {
    1230           0 :                 return false;
    1231             :             }
    1232             :             const char *pszFieldLength =
    1233         617 :                 CPLGetXMLValue(psIter, "field_length", nullptr);
    1234         617 :             if (!pszFieldLength)
    1235             :             {
    1236           0 :                 return false;
    1237             :             }
    1238         617 :             Field f;
    1239         617 :             f.m_nOffset =
    1240         617 :                 nBaseOffset + atoi(pszLoc) - 1;  // Location is 1-based
    1241         617 :             if (f.m_nOffset < 0 || f.m_nOffset >= m_nRecordSize)
    1242             :             {
    1243           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid field_location");
    1244           0 :                 return false;
    1245             :             }
    1246         617 :             f.m_nLength = atoi(pszFieldLength);
    1247        1234 :             if (f.m_nLength <= 0 ||
    1248        1234 :                 f.m_nLength > m_nRecordSize -
    1249         617 :                                   static_cast<int>(m_osLineEnding.size()) -
    1250         617 :                                   f.m_nOffset)
    1251             :             {
    1252           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid field_length");
    1253           0 :                 return false;
    1254             :             }
    1255         617 :             f.m_osDataType = pszDataType;
    1256         617 :             f.m_osUnit = CPLGetXMLValue(psIter, "unit", "");
    1257         617 :             f.m_osDescription = CPLGetXMLValue(psIter, "description", "");
    1258             : 
    1259             :             const char *pszFieldFormat =
    1260         617 :                 CPLGetXMLValue(psIter, "field_format", "");
    1261             : 
    1262             :             CPLXMLNode *psSpecialConstants = const_cast<CPLXMLNode *>(
    1263         617 :                 CPLGetXMLNode(psIter, "Special_Constants"));
    1264         617 :             if (psSpecialConstants)
    1265             :             {
    1266           9 :                 auto psNext = psSpecialConstants->psNext;
    1267           9 :                 psSpecialConstants->psNext = nullptr;
    1268           9 :                 char *pszXML = CPLSerializeXMLTree(psSpecialConstants);
    1269           9 :                 psSpecialConstants->psNext = psNext;
    1270           9 :                 if (pszXML)
    1271             :                 {
    1272           9 :                     f.m_osSpecialConstantsXML = pszXML;
    1273           9 :                     CPLFree(pszXML);
    1274             :                 }
    1275             :             }
    1276             : 
    1277         617 :             m_aoFields.push_back(f);
    1278             : 
    1279         617 :             OGRFieldSubType eSubType = OFSTNone;
    1280         617 :             bool error = false;
    1281         617 :             auto eType = GetFieldTypeFromPDS4DataType(pszDataType, f.m_nLength,
    1282             :                                                       eSubType, error);
    1283         617 :             if (error)
    1284             :             {
    1285           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1286             :                          "Inconsistent field_length w.r.t datatype");
    1287           0 :                 return false;
    1288             :             }
    1289         658 :             if (STARTS_WITH(f.m_osDataType, "ASCII_") && eType == OFTInteger &&
    1290          41 :                 f.m_nLength >= 10)
    1291             :             {
    1292          22 :                 eType = OFTInteger64;
    1293             :             }
    1294         617 :             OGRFieldDefn oFieldDefn((pszName + osSuffixFieldName).c_str(),
    1295        1234 :                                     eType);
    1296         617 :             oFieldDefn.SetSubType(eSubType);
    1297         904 :             if (eType != OFTReal && (STARTS_WITH(f.m_osDataType, "ASCII_") ||
    1298         287 :                                      STARTS_WITH(f.m_osDataType, "UTF_8")))
    1299             :             {
    1300          98 :                 oFieldDefn.SetWidth(f.m_nLength);
    1301             :             }
    1302         519 :             else if ((eType == OFTInteger || eType == OFTInteger64) &&
    1303         268 :                      pszFieldFormat && pszFieldFormat[0] == '%' &&
    1304           9 :                      pszFieldFormat[strlen(pszFieldFormat) - 1] == 'd')
    1305             :             {
    1306           9 :                 oFieldDefn.SetWidth(atoi(pszFieldFormat + 1));
    1307             :             }
    1308         617 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    1309             :         }
    1310        2972 :         else if (psIter->eType == CXT_Element &&
    1311        1486 :                  strcmp(psIter->pszValue,
    1312        2972 :                         ("Group_Field_" + GetSubType()).c_str()) == 0)
    1313             :         {
    1314             :             const char *pszRepetitions =
    1315           1 :                 CPLGetXMLValue(psIter, "repetitions", nullptr);
    1316           1 :             if (!pszRepetitions)
    1317             :             {
    1318           0 :                 return false;
    1319             :             }
    1320             :             const char *pszGroupLocation =
    1321           1 :                 CPLGetXMLValue(psIter, "group_location", nullptr);
    1322           1 :             if (!pszGroupLocation)
    1323             :             {
    1324           0 :                 return false;
    1325             :             }
    1326             :             const char *pszGroupLength =
    1327           1 :                 CPLGetXMLValue(psIter, "group_length", nullptr);
    1328           1 :             if (!pszGroupLength)
    1329             :             {
    1330           0 :                 return false;
    1331             :             }
    1332           1 :             int nRepetitions = std::min(1000, atoi(pszRepetitions));
    1333           1 :             if (nRepetitions <= 0)
    1334             :             {
    1335           0 :                 return false;
    1336             :             }
    1337           1 :             int nGroupOffset =
    1338           1 :                 atoi(pszGroupLocation) - 1;  // Location is 1-based
    1339           1 :             if (nGroupOffset < 0 || nGroupOffset >= m_nRecordSize)
    1340             :             {
    1341           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid group_location");
    1342           0 :                 return false;
    1343             :             }
    1344           1 :             int nGroupLength = atoi(pszGroupLength);
    1345           2 :             if (nGroupLength <= 0 ||
    1346           2 :                 nGroupLength > m_nRecordSize -
    1347           1 :                                    static_cast<int>(m_osLineEnding.size()) -
    1348           2 :                                    nGroupOffset ||
    1349           1 :                 (nGroupLength % nRepetitions) != 0)
    1350             :             {
    1351           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid group_length");
    1352           0 :                 return false;
    1353             :             }
    1354           1 :             int nGroupOneRepetitionLength = nGroupLength / nRepetitions;
    1355         232 :             for (int i = 0; i < nRepetitions; i++)
    1356             :             {
    1357         462 :                 if (!ReadFields(
    1358         231 :                         psIter, nGroupOffset + i * nGroupOneRepetitionLength,
    1359         462 :                         osSuffixFieldName + "_" + CPLSPrintf("%d", i + 1)))
    1360             :                 {
    1361           0 :                     return false;
    1362             :                 }
    1363             :             }
    1364             :         }
    1365             :     }
    1366         264 :     return true;
    1367             : }
    1368             : 
    1369             : /************************************************************************/
    1370             : /*                    RefreshFileAreaObservational()                    */
    1371             : /************************************************************************/
    1372             : 
    1373          14 : void PDS4FixedWidthTable::RefreshFileAreaObservational(CPLXMLNode *psFAO)
    1374             : {
    1375          28 :     CPLString osPrefix;
    1376          14 :     if (STARTS_WITH(psFAO->pszValue, "pds:"))
    1377           0 :         osPrefix = "pds:";
    1378             : 
    1379          28 :     CPLString osDescription;
    1380          14 :     CPLXMLNode *psTable = RefreshFileAreaObservationalBeginningCommon(
    1381          28 :         psFAO, osPrefix, ("Table_" + GetSubType()).c_str(), osDescription);
    1382             : 
    1383          14 :     CPLCreateXMLElementAndValue(psTable, (osPrefix + "records").c_str(),
    1384             :                                 CPLSPrintf(CPL_FRMT_GIB, m_nFeatureCount));
    1385          14 :     if (!osDescription.empty())
    1386           0 :         CPLCreateXMLElementAndValue(psTable, (osPrefix + "description").c_str(),
    1387             :                                     osDescription);
    1388          14 :     if (m_osLineEnding == "\r\n")
    1389             :     {
    1390           8 :         CPLCreateXMLElementAndValue(psTable,
    1391          16 :                                     (osPrefix + "record_delimiter").c_str(),
    1392             :                                     "Carriage-Return Line-Feed");
    1393             :     }
    1394           6 :     else if (m_osLineEnding == "\n")
    1395             :     {
    1396           1 :         CPLCreateXMLElementAndValue(
    1397           2 :             psTable, (osPrefix + "record_delimiter").c_str(), "Line-Feed");
    1398             :     }
    1399             : 
    1400             :     // Write Record_Character / Record_Binary
    1401          14 :     CPLXMLNode *psRecord = CPLCreateXMLNode(
    1402          28 :         psTable, CXT_Element, (osPrefix + "Record_" + GetSubType()).c_str());
    1403          28 :     CPLCreateXMLElementAndValue(
    1404          28 :         psRecord, (osPrefix + "fields").c_str(),
    1405          14 :         CPLSPrintf("%d", static_cast<int>(m_aoFields.size())));
    1406          14 :     CPLCreateXMLElementAndValue(psRecord, (osPrefix + "groups").c_str(), "0");
    1407          28 :     CPLXMLNode *psrecord_length = CPLCreateXMLElementAndValue(
    1408          28 :         psRecord, (osPrefix + "record_length").c_str(),
    1409             :         CPLSPrintf("%d", m_nRecordSize));
    1410          14 :     CPLAddXMLAttributeAndValue(psrecord_length, "unit", "byte");
    1411             : 
    1412          14 :     CPLAssert(static_cast<int>(m_aoFields.size()) ==
    1413             :               m_poRawFeatureDefn->GetFieldCount());
    1414             : 
    1415         163 :     for (int i = 0; i < static_cast<int>(m_aoFields.size()); i++)
    1416             :     {
    1417         149 :         auto &f = m_aoFields[i];
    1418         149 :         auto poFieldDefn = m_poRawFeatureDefn->GetFieldDefn(i);
    1419             : 
    1420             :         CPLXMLNode *psField =
    1421         149 :             CPLCreateXMLNode(psRecord, CXT_Element,
    1422         298 :                              (osPrefix + "Field_" + GetSubType()).c_str());
    1423             : 
    1424         149 :         CPLCreateXMLElementAndValue(psField, (osPrefix + "name").c_str(),
    1425             :                                     poFieldDefn->GetNameRef());
    1426             : 
    1427         298 :         CPLCreateXMLElementAndValue(psField,
    1428         298 :                                     (osPrefix + "field_number").c_str(),
    1429             :                                     CPLSPrintf("%d", i + 1));
    1430             : 
    1431         149 :         auto psfield_location = CPLCreateXMLElementAndValue(
    1432         298 :             psField, (osPrefix + "field_location").c_str(),
    1433         149 :             CPLSPrintf("%d", f.m_nOffset + 1));
    1434         149 :         CPLAddXMLAttributeAndValue(psfield_location, "unit", "byte");
    1435             : 
    1436         149 :         CPLCreateXMLElementAndValue(psField, (osPrefix + "data_type").c_str(),
    1437             :                                     f.m_osDataType.c_str());
    1438             : 
    1439         298 :         auto psfield_length = CPLCreateXMLElementAndValue(
    1440         298 :             psField, (osPrefix + "field_length").c_str(),
    1441             :             CPLSPrintf("%d", f.m_nLength));
    1442         149 :         CPLAddXMLAttributeAndValue(psfield_length, "unit", "byte");
    1443             : 
    1444         149 :         const auto eType(poFieldDefn->GetType());
    1445         149 :         const int nWidth = poFieldDefn->GetWidth();
    1446         149 :         if ((eType == OFTInteger || eType == OFTInteger64) && nWidth > 0)
    1447             :         {
    1448           8 :             CPLCreateXMLElementAndValue(psField,
    1449           8 :                                         (osPrefix + "field_format").c_str(),
    1450             :                                         CPLSPrintf("%%%dd", nWidth));
    1451             :         }
    1452             : 
    1453         149 :         if (!f.m_osUnit.empty())
    1454             :         {
    1455          30 :             CPLCreateXMLElementAndValue(psField, (osPrefix + "unit").c_str(),
    1456          30 :                                         m_aoFields[i].m_osUnit.c_str());
    1457             :         }
    1458             : 
    1459         149 :         if (!f.m_osDescription.empty())
    1460             :         {
    1461         132 :             CPLCreateXMLElementAndValue(psField,
    1462         132 :                                         (osPrefix + "description").c_str(),
    1463          66 :                                         m_aoFields[i].m_osDescription.c_str());
    1464             :         }
    1465         149 :         if (!f.m_osSpecialConstantsXML.empty())
    1466             :         {
    1467             :             auto psSpecialConstants =
    1468           3 :                 CPLParseXMLString(f.m_osSpecialConstantsXML);
    1469           3 :             if (psSpecialConstants)
    1470             :             {
    1471           3 :                 CPLAddXMLChild(psField, psSpecialConstants);
    1472             :             }
    1473             :         }
    1474             :     }
    1475          14 : }
    1476             : 
    1477             : /************************************************************************/
    1478             : /*                            CreateField()                             */
    1479             : /************************************************************************/
    1480             : 
    1481         115 : OGRErr PDS4FixedWidthTable::CreateField(const OGRFieldDefn *poFieldIn, int)
    1482             : 
    1483             : {
    1484         115 :     if (m_poDS->GetAccess() != GA_Update)
    1485             :     {
    1486           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1487             :                  "Dataset opened in read-only mode");
    1488           0 :         return OGRERR_FAILURE;
    1489             :     }
    1490         115 :     if (m_nFeatureCount > 0)
    1491             :     {
    1492           0 :         return OGRERR_FAILURE;
    1493             :     }
    1494             : 
    1495         230 :     Field f;
    1496         115 :     if (!m_aoFields.empty())
    1497             :     {
    1498         106 :         f.m_nOffset = m_aoFields.back().m_nOffset + m_aoFields.back().m_nLength;
    1499             :     }
    1500             : 
    1501         115 :     if (!CreateFieldInternal(poFieldIn->GetType(), poFieldIn->GetSubType(),
    1502         115 :                              poFieldIn->GetWidth(), f))
    1503             :     {
    1504           0 :         return OGRERR_FAILURE;
    1505             :     }
    1506             : 
    1507         115 :     MarkHeaderDirty();
    1508         115 :     m_aoFields.push_back(f);
    1509         115 :     m_poRawFeatureDefn->AddFieldDefn(poFieldIn);
    1510         115 :     m_poFeatureDefn->AddFieldDefn(poFieldIn);
    1511         115 :     m_nRecordSize += f.m_nLength;
    1512         115 :     m_osBuffer.resize(m_nRecordSize);
    1513             : 
    1514         115 :     return OGRERR_NONE;
    1515             : }
    1516             : 
    1517             : /************************************************************************/
    1518             : /*                         InitializeNewLayer()                         */
    1519             : /************************************************************************/
    1520             : 
    1521          13 : bool PDS4FixedWidthTable::InitializeNewLayer(const OGRSpatialReference *poSRS,
    1522             :                                              bool bForceGeographic,
    1523             :                                              OGRwkbGeometryType eGType,
    1524             :                                              const char *const *papszOptions)
    1525             : {
    1526          13 :     CPLAssert(m_fp == nullptr);
    1527          13 :     m_fp = VSIFOpenL(m_osFilename, "wb+");
    1528          13 :     if (!m_fp)
    1529             :     {
    1530           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
    1531             :                  m_osFilename.c_str());
    1532           0 :         return false;
    1533             :     }
    1534          13 :     m_aosLCO.Assign(CSLDuplicate(papszOptions));
    1535             : 
    1536          13 :     m_nRecordSize = 0;
    1537             : 
    1538             :     const char *pszGeomColumns =
    1539          13 :         CSLFetchNameValueDef(papszOptions, "GEOM_COLUMNS", "AUTO");
    1540          13 :     if (EQUAL(pszGeomColumns, "WKT"))
    1541             :     {
    1542           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1543             :                  "GEOM_COLUMNS=WKT only supported for delimited/CSV tables");
    1544             :     }
    1545             : 
    1546          13 :     if ((EQUAL(pszGeomColumns, "AUTO") && wkbFlatten(eGType) == wkbPoint &&
    1547          26 :          (bForceGeographic || (poSRS && poSRS->IsGeographic()))) ||
    1548           9 :         (EQUAL(pszGeomColumns, "LONG_LAT") && eGType != wkbNone))
    1549             :     {
    1550             :         {
    1551             :             OGRFieldDefn oFieldDefn(
    1552           8 :                 CSLFetchNameValueDef(papszOptions, "LAT", "Latitude"), OFTReal);
    1553           4 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    1554           4 :             m_iLatField = m_poRawFeatureDefn->GetFieldCount() - 1;
    1555           4 :             Field f;
    1556           4 :             f.m_nOffset = m_aoFields.empty() ? 0
    1557           0 :                                              : m_aoFields.back().m_nOffset +
    1558           0 :                                                    m_aoFields.back().m_nLength;
    1559           4 :             CreateFieldInternal(OFTReal, OFSTNone, 0, f);
    1560           4 :             m_aoFields.push_back(f);
    1561           4 :             m_nRecordSize += f.m_nLength;
    1562             :         }
    1563             :         {
    1564             :             OGRFieldDefn oFieldDefn(
    1565             :                 CSLFetchNameValueDef(papszOptions, "LONG", "Longitude"),
    1566           8 :                 OFTReal);
    1567           4 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    1568           4 :             m_iLongField = m_poRawFeatureDefn->GetFieldCount() - 1;
    1569           4 :             Field f;
    1570           4 :             f.m_nOffset =
    1571           4 :                 m_aoFields.back().m_nOffset + m_aoFields.back().m_nLength;
    1572           4 :             CreateFieldInternal(OFTReal, OFSTNone, 0, f);
    1573           4 :             m_aoFields.push_back(f);
    1574           4 :             m_nRecordSize += f.m_nLength;
    1575             :         }
    1576           4 :         if (eGType == wkbPoint25D)
    1577             :         {
    1578             :             OGRFieldDefn oFieldDefn(
    1579           8 :                 CSLFetchNameValueDef(papszOptions, "ALT", "Altitude"), OFTReal);
    1580           4 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    1581           4 :             m_iAltField = m_poRawFeatureDefn->GetFieldCount() - 1;
    1582           4 :             Field f;
    1583           4 :             f.m_nOffset =
    1584           4 :                 m_aoFields.back().m_nOffset + m_aoFields.back().m_nLength;
    1585           4 :             CreateFieldInternal(OFTReal, OFSTNone, 0, f);
    1586           4 :             m_aoFields.push_back(f);
    1587           4 :             m_nRecordSize += f.m_nLength;
    1588             :         }
    1589             : 
    1590           4 :         m_poRawFeatureDefn->SetGeomType(eGType);
    1591             : 
    1592           4 :         m_poFeatureDefn->SetGeomType(eGType);
    1593           4 :         if (poSRS)
    1594             :         {
    1595           2 :             auto poSRSClone = poSRS->Clone();
    1596           2 :             poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1597           2 :             m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
    1598           2 :             poSRSClone->Release();
    1599             :         }
    1600             :     }
    1601             : 
    1602          13 :     if (GetSubType() == "Character")
    1603             :     {
    1604           8 :         ParseLineEndingOption(papszOptions);
    1605             :     }
    1606          13 :     m_nRecordSize += static_cast<int>(m_osLineEnding.size());
    1607          13 :     m_osBuffer.resize(m_nRecordSize);
    1608             : 
    1609          13 :     m_nFeatureCount = 0;
    1610          13 :     MarkHeaderDirty();
    1611          13 :     return true;
    1612             : }
    1613             : 
    1614             : /************************************************************************/
    1615             : /* ==================================================================== */
    1616             : /*                         PDS4TableCharacter                           */
    1617             : /* ==================================================================== */
    1618             : /************************************************************************/
    1619             : 
    1620          30 : PDS4TableCharacter::PDS4TableCharacter(PDS4Dataset *poDS, const char *pszName,
    1621          30 :                                        const char *pszFilename, bool bUpdate)
    1622          30 :     : PDS4FixedWidthTable(poDS, pszName, pszFilename, bUpdate)
    1623             : {
    1624          30 : }
    1625             : 
    1626             : /************************************************************************/
    1627             : /*                        CreateFieldInternal()                         */
    1628             : /************************************************************************/
    1629             : 
    1630          80 : bool PDS4TableCharacter::CreateFieldInternal(OGRFieldType eType,
    1631             :                                              OGRFieldSubType eSubType,
    1632             :                                              int nWidth, Field &f)
    1633             : {
    1634          80 :     if (nWidth > 0)
    1635             :     {
    1636           0 :         f.m_nLength = nWidth;
    1637             :     }
    1638             :     else
    1639             :     {
    1640          80 :         if (eType == OFTString)
    1641             :         {
    1642           4 :             f.m_nLength = 64;
    1643             :         }
    1644          76 :         else if (eType == OFTInteger)
    1645             :         {
    1646           9 :             f.m_nLength = eSubType == OFSTBoolean ? 1 : 11;
    1647             :         }
    1648          67 :         else if (eType == OFTInteger64)
    1649             :         {
    1650           4 :             f.m_nLength = 21;
    1651             :         }
    1652          63 :         else if (eType == OFTReal)
    1653             :         {
    1654          51 :             f.m_nLength = 16;
    1655             :         }
    1656          12 :         else if (eType == OFTDateTime)
    1657             :         {
    1658             :             // YYYY-MM-DDTHH:MM:SS.sssZ
    1659           4 :             f.m_nLength = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3 + 1;
    1660             :         }
    1661           8 :         else if (eType == OFTDate)
    1662             :         {
    1663             :             // YYYY-MM-DD
    1664           4 :             f.m_nLength = 4 + 1 + 2 + 1 + 2;
    1665             :         }
    1666           4 :         else if (eType == OFTTime)
    1667             :         {
    1668             :             // HH:MM:SS.sss
    1669           4 :             f.m_nLength = 2 + 1 + 2 + 1 + 2 + 1 + 3;
    1670             :         }
    1671             :     }
    1672          80 :     if (eType == OFTString)
    1673             :     {
    1674           4 :         f.m_osDataType = "UTF8_String";
    1675             :     }
    1676          76 :     else if (eType == OFTInteger)
    1677             :     {
    1678             :         f.m_osDataType =
    1679           9 :             eSubType == OFSTBoolean ? "ASCII_Boolean" : "ASCII_Integer";
    1680             :     }
    1681          67 :     else if (eType == OFTInteger64)
    1682             :     {
    1683           4 :         f.m_osDataType = "ASCII_Integer";
    1684             :     }
    1685          63 :     else if (eType == OFTReal)
    1686             :     {
    1687          51 :         f.m_osDataType = "ASCII_Real";
    1688             :     }
    1689          12 :     else if (eType == OFTDateTime)
    1690             :     {
    1691           4 :         f.m_osDataType = "ASCII_Date_Time_YMD";
    1692             :     }
    1693           8 :     else if (eType == OFTDate)
    1694             :     {
    1695           4 :         f.m_osDataType = "ASCII_Date_YMD";
    1696             :     }
    1697           4 :     else if (eType == OFTTime)
    1698             :     {
    1699           4 :         f.m_osDataType = "ASCII_Time";
    1700             :     }
    1701             :     else
    1702             :     {
    1703           0 :         return false;
    1704             :     }
    1705          80 :     return true;
    1706             : }
    1707             : 
    1708             : /************************************************************************/
    1709             : /* ==================================================================== */
    1710             : /*                         PDS4TableBinary                              */
    1711             : /* ==================================================================== */
    1712             : /************************************************************************/
    1713             : 
    1714          16 : PDS4TableBinary::PDS4TableBinary(PDS4Dataset *poDS, const char *pszName,
    1715          16 :                                  const char *pszFilename, bool bUpdate)
    1716          16 :     : PDS4FixedWidthTable(poDS, pszName, pszFilename, bUpdate)
    1717             : {
    1718          16 : }
    1719             : 
    1720             : /************************************************************************/
    1721             : /*                        CreateFieldInternal()                         */
    1722             : /************************************************************************/
    1723             : 
    1724          47 : bool PDS4TableBinary::CreateFieldInternal(OGRFieldType eType,
    1725             :                                           OGRFieldSubType eSubType, int nWidth,
    1726             :                                           Field &f)
    1727             : {
    1728          94 :     CPLString osEndianness(CPLGetConfigOption("PDS4_ENDIANNESS", "LSB"));
    1729          94 :     CPLString osSignedness(CPLGetConfigOption("PDS4_SIGNEDNESS", "Signed"));
    1730             : 
    1731          47 :     if (eType == OFTString)
    1732             :     {
    1733           4 :         f.m_osDataType = "UTF8_String";
    1734           4 :         f.m_nLength = nWidth > 0 ? nWidth : 64;
    1735             :     }
    1736          43 :     else if (eType == OFTInteger)
    1737             :     {
    1738          28 :         f.m_osDataType = nWidth > 0 && nWidth <= 2 ? osSignedness + "Byte"
    1739           8 :                          : eSubType == OFSTBoolean ? CPLString("ASCII_Boolean")
    1740             :                          : eSubType == OFSTInt16
    1741          20 :                              ? osSignedness + osEndianness + "2"
    1742          20 :                              : osSignedness + osEndianness + "4";
    1743          28 :         f.m_nLength = nWidth > 0 && nWidth <= 2 ? 1
    1744          20 :                       : eSubType == OFSTBoolean ? 1
    1745           8 :                       : eSubType == OFSTInt16   ? 2
    1746             :                                                 : 4;
    1747             :     }
    1748          27 :     else if (eType == OFTInteger64)
    1749             :     {
    1750           4 :         f.m_osDataType = osSignedness + osEndianness + "8";
    1751           4 :         f.m_nLength = 8;
    1752             :     }
    1753          23 :     else if (eType == OFTReal)
    1754             :     {
    1755             :         f.m_osDataType = eSubType == OFSTFloat32
    1756          22 :                              ? "IEEE754" + osEndianness + "Single"
    1757          18 :                              : "IEEE754" + osEndianness + "Double";
    1758          11 :         f.m_nLength = eSubType == OFSTFloat32 ? 4 : 8;
    1759             :     }
    1760          12 :     else if (eType == OFTDateTime)
    1761             :     {
    1762           4 :         f.m_osDataType = "ASCII_Date_Time_YMD";
    1763             :         // YYYY-MM-DDTHH:MM:SS.sssZ
    1764           4 :         f.m_nLength = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3 + 1;
    1765             :     }
    1766           8 :     else if (eType == OFTDate)
    1767             :     {
    1768           4 :         f.m_osDataType = "ASCII_Date_YMD";
    1769             :         // YYYY-MM-DD
    1770           4 :         f.m_nLength = 4 + 1 + 2 + 1 + 2;
    1771             :     }
    1772           4 :     else if (eType == OFTTime)
    1773             :     {
    1774           4 :         f.m_osDataType = "ASCII_Time";
    1775             :         // HH:MM:SS.sss
    1776           4 :         f.m_nLength = 2 + 1 + 2 + 1 + 2 + 1 + 3;
    1777             :     }
    1778             :     else
    1779             :     {
    1780           0 :         return false;
    1781             :     }
    1782          47 :     return true;
    1783             : }
    1784             : 
    1785             : /************************************************************************/
    1786             : /* ==================================================================== */
    1787             : /*                         PDS4DelimitedTable                           */
    1788             : /* ==================================================================== */
    1789             : /************************************************************************/
    1790             : 
    1791         120 : PDS4DelimitedTable::PDS4DelimitedTable(PDS4Dataset *poDS, const char *pszName,
    1792         120 :                                        const char *pszFilename, bool bUpdate)
    1793         120 :     : PDS4TableBaseLayer(poDS, pszName, pszFilename, bUpdate)
    1794             : {
    1795         120 : }
    1796             : 
    1797             : /************************************************************************/
    1798             : /*                        ~PDS4DelimitedTable()                         */
    1799             : /************************************************************************/
    1800             : 
    1801         240 : PDS4DelimitedTable::~PDS4DelimitedTable()
    1802             : {
    1803         120 :     if (m_bDirtyHeader)
    1804          55 :         GenerateVRT();
    1805         240 : }
    1806             : 
    1807             : /************************************************************************/
    1808             : /*                            GenerateVRT()                             */
    1809             : /************************************************************************/
    1810             : 
    1811          55 : void PDS4DelimitedTable::GenerateVRT()
    1812             : {
    1813          55 :     CPLString osVRTFilename = CPLResetExtensionSafe(m_osFilename, "vrt");
    1814          55 :     if (m_bCreation)
    1815             :     {
    1816             :         // In creation mode, generate the VRT, unless explicitly disabled by
    1817             :         // CREATE_VRT=NO
    1818          55 :         if (!m_aosLCO.FetchBool("CREATE_VRT", true))
    1819           1 :             return;
    1820             :     }
    1821             :     else
    1822             :     {
    1823             :         // In a update situation, only generates the VRT if ones already exists
    1824             :         VSIStatBufL sStat;
    1825           0 :         if (VSIStatL(osVRTFilename, &sStat) != 0)
    1826           0 :             return;
    1827             :     }
    1828             : 
    1829             :     CPLXMLNode *psRoot =
    1830          54 :         CPLCreateXMLNode(nullptr, CXT_Element, "OGRVRTDataSource");
    1831          54 :     CPLXMLNode *psLayer = CPLCreateXMLNode(psRoot, CXT_Element, "OGRVRTLayer");
    1832          54 :     CPLAddXMLAttributeAndValue(psLayer, "name", GetName());
    1833             : 
    1834          54 :     CPLXMLNode *psSrcDataSource = CPLCreateXMLElementAndValue(
    1835             :         psLayer, "SrcDataSource", CPLGetFilename(m_osFilename));
    1836          54 :     CPLAddXMLAttributeAndValue(psSrcDataSource, "relativeToVRT", "1");
    1837             : 
    1838          54 :     CPLCreateXMLElementAndValue(
    1839         108 :         psLayer, "SrcLayer", CPLGetBasenameSafe(m_osFilename.c_str()).c_str());
    1840             : 
    1841          54 :     CPLXMLNode *psLastChild = CPLCreateXMLElementAndValue(
    1842             :         psLayer, "GeometryType",
    1843         108 :         OGRVRTGetSerializedGeometryType(GetGeomType()).c_str());
    1844             : 
    1845          54 :     if (GetSpatialRef())
    1846             :     {
    1847           1 :         char *pszWKT = nullptr;
    1848           1 :         GetSpatialRef()->exportToWkt(&pszWKT);
    1849           1 :         if (pszWKT)
    1850             :         {
    1851           1 :             CPLCreateXMLElementAndValue(psLayer, "LayerSRS", pszWKT);
    1852           1 :             CPLFree(pszWKT);
    1853             :         }
    1854             :     }
    1855             : 
    1856          55 :     while (psLastChild->psNext)
    1857           1 :         psLastChild = psLastChild->psNext;
    1858          54 :     const int nFieldCount = m_poRawFeatureDefn->GetFieldCount();
    1859         221 :     for (int i = 0; i < nFieldCount; i++)
    1860             :     {
    1861         167 :         if (i != m_iWKT && i != m_iLongField && i != m_iLatField &&
    1862         132 :             i != m_iAltField)
    1863             :         {
    1864         132 :             OGRFieldDefn *poFieldDefn = m_poRawFeatureDefn->GetFieldDefn(i);
    1865             :             CPLXMLNode *psField =
    1866         132 :                 CPLCreateXMLNode(nullptr, CXT_Element, "Field");
    1867         132 :             psLastChild->psNext = psField;
    1868         132 :             psLastChild = psField;
    1869         132 :             CPLAddXMLAttributeAndValue(psField, "name",
    1870             :                                        poFieldDefn->GetNameRef());
    1871         132 :             CPLAddXMLAttributeAndValue(
    1872             :                 psField, "type", OGR_GetFieldTypeName(poFieldDefn->GetType()));
    1873         132 :             if (poFieldDefn->GetSubType() != OFSTNone)
    1874             :             {
    1875           4 :                 CPLAddXMLAttributeAndValue(
    1876             :                     psField, "subtype",
    1877             :                     OGR_GetFieldSubTypeName(poFieldDefn->GetSubType()));
    1878             :             }
    1879         133 :             if (poFieldDefn->GetWidth() > 0 &&
    1880           1 :                 poFieldDefn->GetType() != OFTReal)
    1881             :             {
    1882           1 :                 CPLAddXMLAttributeAndValue(
    1883             :                     psField, "width",
    1884             :                     CPLSPrintf("%d", poFieldDefn->GetWidth()));
    1885             :             }
    1886         132 :             CPLAddXMLAttributeAndValue(psField, "src",
    1887             :                                        poFieldDefn->GetNameRef());
    1888             :         }
    1889             :     }
    1890             : 
    1891          54 :     if (m_iWKT >= 0)
    1892             :     {
    1893             :         CPLXMLNode *psField =
    1894          35 :             CPLCreateXMLNode(nullptr, CXT_Element, "GeometryField");
    1895          35 :         psLastChild->psNext = psField;
    1896          35 :         psLastChild = psField;
    1897          35 :         CPLAddXMLAttributeAndValue(psField, "encoding", "WKT");
    1898          35 :         CPLAddXMLAttributeAndValue(
    1899             :             psField, "field",
    1900          35 :             m_poRawFeatureDefn->GetFieldDefn(m_iWKT)->GetNameRef());
    1901             :     }
    1902          19 :     else if (m_iLongField >= 0 && m_iLatField >= 0)
    1903             :     {
    1904             :         CPLXMLNode *psField =
    1905           0 :             CPLCreateXMLNode(nullptr, CXT_Element, "GeometryField");
    1906           0 :         psLastChild->psNext = psField;
    1907           0 :         psLastChild = psField;
    1908           0 :         CPLAddXMLAttributeAndValue(psField, "encoding", "PointFromColumns");
    1909           0 :         CPLAddXMLAttributeAndValue(
    1910             :             psField, "x",
    1911           0 :             m_poRawFeatureDefn->GetFieldDefn(m_iLongField)->GetNameRef());
    1912           0 :         CPLAddXMLAttributeAndValue(
    1913             :             psField, "y",
    1914           0 :             m_poRawFeatureDefn->GetFieldDefn(m_iLatField)->GetNameRef());
    1915           0 :         if (m_iAltField >= 0)
    1916             :         {
    1917           0 :             CPLAddXMLAttributeAndValue(
    1918             :                 psField, "z",
    1919           0 :                 m_poRawFeatureDefn->GetFieldDefn(m_iAltField)->GetNameRef());
    1920             :         }
    1921             :     }
    1922             : 
    1923          54 :     CPL_IGNORE_RET_VAL(psLastChild);
    1924             : 
    1925          54 :     CPLSerializeXMLTreeToFile(psRoot, osVRTFilename);
    1926          54 :     CPLDestroyXMLNode(psRoot);
    1927             : }
    1928             : 
    1929             : /************************************************************************/
    1930             : /*                            ResetReading()                            */
    1931             : /************************************************************************/
    1932             : 
    1933         214 : void PDS4DelimitedTable::ResetReading()
    1934             : {
    1935         214 :     m_nFID = 1;
    1936         214 :     VSIFSeekL(m_fp, m_nOffset, SEEK_SET);
    1937         214 : }
    1938             : 
    1939             : /************************************************************************/
    1940             : /*                         GetNextFeatureRaw()                          */
    1941             : /************************************************************************/
    1942             : 
    1943         506 : OGRFeature *PDS4DelimitedTable::GetNextFeatureRaw()
    1944             : {
    1945         506 :     const char *pszLine = CPLReadLine2L(m_fp, 10 * 1024 * 1024, nullptr);
    1946         506 :     if (pszLine == nullptr)
    1947             :     {
    1948          47 :         return nullptr;
    1949             :     }
    1950             : 
    1951         459 :     char szDelimiter[2] = {m_chFieldDelimiter, 0};
    1952         459 :     char **papszFields = CSLTokenizeString2(
    1953             :         pszLine, szDelimiter, CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS);
    1954         459 :     if (CSLCount(papszFields) != m_poRawFeatureDefn->GetFieldCount())
    1955             :     {
    1956           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1957             :                  "Did not get expected number of fields at line " CPL_FRMT_GIB,
    1958             :                  m_nFID);
    1959             :     }
    1960             : 
    1961         459 :     OGRFeature *poRawFeature = new OGRFeature(m_poRawFeatureDefn);
    1962         459 :     poRawFeature->SetFID(m_nFID);
    1963         459 :     m_nFID++;
    1964        4231 :     for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount() && papszFields &&
    1965        1886 :                     papszFields[i];
    1966             :          i++)
    1967             :     {
    1968        1886 :         if (!m_aoFields[i].m_osMissingConstant.empty() &&
    1969           0 :             m_aoFields[i].m_osMissingConstant == papszFields[i])
    1970             :         {
    1971             :             // do nothing
    1972             :         }
    1973        1886 :         else if (m_aoFields[i].m_osDataType == "ASCII_Boolean")
    1974             :         {
    1975          10 :             poRawFeature->SetField(i, EQUAL(papszFields[i], "t") ||
    1976           5 :                                               EQUAL(papszFields[i], "1")
    1977             :                                           ? 1
    1978             :                                           : 0);
    1979             :         }
    1980             :         else
    1981             :         {
    1982        1881 :             poRawFeature->SetField(i, papszFields[i]);
    1983             :         }
    1984             :     }
    1985             : 
    1986         459 :     CSLDestroy(papszFields);
    1987             : 
    1988         459 :     OGRFeature *poFeature = AddGeometryFromFields(poRawFeature);
    1989         459 :     delete poRawFeature;
    1990         459 :     return poFeature;
    1991             : }
    1992             : 
    1993             : /************************************************************************/
    1994             : /*                           GetNextFeature()                           */
    1995             : /************************************************************************/
    1996             : 
    1997         506 : OGRFeature *PDS4DelimitedTable::GetNextFeature()
    1998             : {
    1999             :     while (true)
    2000             :     {
    2001         506 :         auto poFeature = GetNextFeatureRaw();
    2002         506 :         if (poFeature == nullptr)
    2003             :         {
    2004          47 :             return nullptr;
    2005             :         }
    2006             : 
    2007        1028 :         if ((m_poFilterGeom == nullptr ||
    2008         856 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    2009         397 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    2010             :         {
    2011         397 :             return poFeature;
    2012             :         }
    2013          62 :         delete poFeature;
    2014          62 :     }
    2015             : }
    2016             : 
    2017             : /************************************************************************/
    2018             : /*                           TestCapability()                           */
    2019             : /************************************************************************/
    2020             : 
    2021         358 : bool PDS4DelimitedTable::TestCapability(const char *pszCap) const
    2022             : {
    2023         358 :     if (EQUAL(pszCap, OLCRandomRead) || EQUAL(pszCap, OLCStringsAsUTF8) ||
    2024         344 :         EQUAL(pszCap, OLCZGeometries))
    2025             :     {
    2026          17 :         return true;
    2027             :     }
    2028         341 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    2029             :     {
    2030           0 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
    2031             :     }
    2032         341 :     if (EQUAL(pszCap, OLCCreateField))
    2033             :     {
    2034         130 :         return m_poDS->GetAccess() == GA_Update && m_nFeatureCount == 0;
    2035             :     }
    2036         211 :     if (EQUAL(pszCap, OLCSequentialWrite))
    2037             :     {
    2038          86 :         return m_poDS->GetAccess() == GA_Update;
    2039             :     }
    2040         125 :     return false;
    2041             : }
    2042             : 
    2043             : /************************************************************************/
    2044             : /*                           QuoteIfNeeded()                            */
    2045             : /************************************************************************/
    2046             : 
    2047         535 : CPLString PDS4DelimitedTable::QuoteIfNeeded(const char *pszVal)
    2048             : {
    2049         535 :     if (strchr(pszVal, m_chFieldDelimiter) == nullptr)
    2050             :     {
    2051         489 :         return pszVal;
    2052             :     }
    2053          92 :     return '"' + CPLString(pszVal) + '"';
    2054             : }
    2055             : 
    2056             : /************************************************************************/
    2057             : /*                           ICreateFeature()                           */
    2058             : /************************************************************************/
    2059             : 
    2060          97 : OGRErr PDS4DelimitedTable::ICreateFeature(OGRFeature *poFeature)
    2061             : {
    2062          97 :     if (!m_bUpdate)
    2063             :     {
    2064           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2065             :                  "Operation not supported on read-only layer");
    2066           0 :         return OGRERR_FAILURE;
    2067             :     }
    2068             : 
    2069          97 :     if (m_bAddWKTColumnPending)
    2070             :     {
    2071             :         OGRFieldDefn oFieldDefn(
    2072          72 :             CSLFetchNameValueDef(m_aosLCO.List(), "WKT", "WKT"), OFTString);
    2073          36 :         m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    2074          36 :         m_iWKT = m_poRawFeatureDefn->GetFieldCount() - 1;
    2075          36 :         Field f;
    2076          36 :         f.m_osDataType = "ASCII_String";
    2077          36 :         m_aoFields.push_back(std::move(f));
    2078          36 :         m_bAddWKTColumnPending = false;
    2079             :     }
    2080             : 
    2081          97 :     if (m_nFeatureCount == 0)
    2082             :     {
    2083         208 :         for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
    2084             :         {
    2085         169 :             if (i > 0)
    2086             :             {
    2087         130 :                 VSIFPrintfL(m_fp, "%c", m_chFieldDelimiter);
    2088             :             }
    2089         169 :             VSIFPrintfL(
    2090             :                 m_fp, "%s",
    2091         338 :                 QuoteIfNeeded(m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef())
    2092             :                     .c_str());
    2093             :         }
    2094          39 :         VSIFPrintfL(m_fp, "%s", m_osLineEnding.c_str());
    2095          39 :         m_nOffset = VSIFTellL(m_fp);
    2096             :     }
    2097             : 
    2098          97 :     OGRFeature *poRawFeature = AddFieldsFromGeometry(poFeature);
    2099         591 :     for (int i = 0; i < m_poRawFeatureDefn->GetFieldCount(); i++)
    2100             :     {
    2101         494 :         if (i > 0)
    2102             :         {
    2103         397 :             VSIFPrintfL(m_fp, "%c", m_chFieldDelimiter);
    2104             :         }
    2105         494 :         if (!poRawFeature->IsFieldSetAndNotNull(i))
    2106             :         {
    2107         128 :             if (!m_aoFields[i].m_osMissingConstant.empty())
    2108             :             {
    2109           0 :                 VSIFPrintfL(
    2110             :                     m_fp, "%s",
    2111           0 :                     QuoteIfNeeded(m_aoFields[i].m_osMissingConstant).c_str());
    2112             :             }
    2113         128 :             continue;
    2114             :         }
    2115         366 :         VSIFPrintfL(m_fp, "%s",
    2116         732 :                     QuoteIfNeeded(poRawFeature->GetFieldAsString(i)).c_str());
    2117             :     }
    2118          97 :     VSIFPrintfL(m_fp, "%s", m_osLineEnding.c_str());
    2119          97 :     delete poRawFeature;
    2120             : 
    2121          97 :     m_nFeatureCount++;
    2122          97 :     poFeature->SetFID(m_nFeatureCount);
    2123             : 
    2124          97 :     return OGRERR_NONE;
    2125             : }
    2126             : 
    2127             : /************************************************************************/
    2128             : /*                            CreateField()                             */
    2129             : /************************************************************************/
    2130             : 
    2131         133 : OGRErr PDS4DelimitedTable::CreateField(const OGRFieldDefn *poFieldIn, int)
    2132             : 
    2133             : {
    2134         133 :     if (m_poDS->GetAccess() != GA_Update)
    2135             :     {
    2136           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2137             :                  "Dataset opened in read-only mode");
    2138           0 :         return OGRERR_FAILURE;
    2139             :     }
    2140         133 :     if (m_nFeatureCount > 0)
    2141             :     {
    2142           0 :         return OGRERR_FAILURE;
    2143             :     }
    2144             : 
    2145         133 :     const auto eType = poFieldIn->GetType();
    2146         266 :     Field f;
    2147         133 :     if (eType == OFTString)
    2148             :     {
    2149          37 :         f.m_osDataType = "UTF8_String";
    2150             :     }
    2151          96 :     else if (eType == OFTInteger)
    2152             :     {
    2153          26 :         f.m_osDataType = poFieldIn->GetSubType() == OFSTBoolean
    2154             :                              ? "ASCII_Boolean"
    2155          26 :                              : "ASCII_Integer";
    2156             :     }
    2157          70 :     else if (eType == OFTInteger64)
    2158             :     {
    2159           5 :         f.m_osDataType = "ASCII_Integer";
    2160             :     }
    2161          65 :     else if (eType == OFTReal)
    2162             :     {
    2163          21 :         f.m_osDataType = "ASCII_Real";
    2164             :     }
    2165          44 :     else if (eType == OFTDateTime)
    2166             :     {
    2167          20 :         f.m_osDataType = "ASCII_Date_Time_YMD";
    2168             :     }
    2169          24 :     else if (eType == OFTDate)
    2170             :     {
    2171          20 :         f.m_osDataType = "ASCII_Date_YMD";
    2172             :     }
    2173           4 :     else if (eType == OFTTime)
    2174             :     {
    2175           4 :         f.m_osDataType = "ASCII_Time";
    2176             :     }
    2177             :     else
    2178             :     {
    2179           0 :         return OGRERR_FAILURE;
    2180             :     }
    2181             : 
    2182         133 :     MarkHeaderDirty();
    2183         133 :     m_aoFields.push_back(std::move(f));
    2184         133 :     m_poRawFeatureDefn->AddFieldDefn(poFieldIn);
    2185         133 :     m_poFeatureDefn->AddFieldDefn(poFieldIn);
    2186             : 
    2187         133 :     return OGRERR_NONE;
    2188             : }
    2189             : 
    2190             : /************************************************************************/
    2191             : /*                            ReadTableDef()                            */
    2192             : /************************************************************************/
    2193             : 
    2194          64 : bool PDS4DelimitedTable::ReadTableDef(const CPLXMLNode *psTable)
    2195             : {
    2196          64 :     CPLAssert(m_fp == nullptr);
    2197          64 :     m_fp = VSIFOpenL(m_osFilename,
    2198          64 :                      (m_poDS->GetAccess() == GA_ReadOnly) ? "rb" : "r+b");
    2199          64 :     if (!m_fp)
    2200             :     {
    2201           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
    2202             :                  m_osFilename.c_str());
    2203           0 :         return false;
    2204             :     }
    2205             : 
    2206          64 :     m_nOffset = static_cast<GUIntBig>(
    2207          64 :         CPLAtoGIntBig(CPLGetXMLValue(psTable, "offset", "0")));
    2208             : 
    2209          64 :     m_nFeatureCount = CPLAtoGIntBig(CPLGetXMLValue(psTable, "records", "-1"));
    2210             : 
    2211             :     const char *pszRecordDelimiter =
    2212          64 :         CPLGetXMLValue(psTable, "record_delimiter", "");
    2213          64 :     if (EQUAL(pszRecordDelimiter, "Carriage-Return Line-Feed"))
    2214          62 :         m_osLineEnding = "\r\n";
    2215           2 :     else if (EQUAL(pszRecordDelimiter, "Line-Feed"))
    2216           2 :         m_osLineEnding = "\n";
    2217           0 :     else if (EQUAL(pszRecordDelimiter, ""))
    2218             :     {
    2219           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing record_delimiter");
    2220           0 :         return false;
    2221             :     }
    2222             :     else
    2223             :     {
    2224           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid record_delimiter");
    2225           0 :         return false;
    2226             :     }
    2227             : 
    2228             :     const char *pszFieldDelimiter =
    2229          64 :         CPLGetXMLValue(psTable, "field_delimiter", nullptr);
    2230          64 :     if (pszFieldDelimiter == nullptr)
    2231             :     {
    2232           0 :         return false;
    2233             :     }
    2234          64 :     if (EQUAL(pszFieldDelimiter, "Comma"))
    2235             :     {
    2236          64 :         m_chFieldDelimiter = ',';
    2237             :     }
    2238           0 :     else if (EQUAL(pszFieldDelimiter, "Horizontal Tab"))
    2239             :     {
    2240           0 :         m_chFieldDelimiter = '\t';
    2241             :     }
    2242           0 :     else if (EQUAL(pszFieldDelimiter, "Semicolon"))
    2243             :     {
    2244           0 :         m_chFieldDelimiter = ';';
    2245             :     }
    2246           0 :     else if (EQUAL(pszFieldDelimiter, "Vertical Bar"))
    2247             :     {
    2248           0 :         m_chFieldDelimiter = '|';
    2249             :     }
    2250             :     else
    2251             :     {
    2252           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2253             :                  "field_delimiter value not supported");
    2254           0 :         return false;
    2255             :     }
    2256             : 
    2257          64 :     const CPLXMLNode *psRecord = CPLGetXMLNode(psTable, "Record_Delimited");
    2258          64 :     if (!psRecord)
    2259             :     {
    2260           0 :         return false;
    2261             :     }
    2262          64 :     if (!ReadFields(psRecord, ""))
    2263             :     {
    2264           0 :         return false;
    2265             :     }
    2266             : 
    2267          64 :     SetupGeomField();
    2268          64 :     ResetReading();
    2269             : 
    2270          64 :     return true;
    2271             : }
    2272             : 
    2273             : /************************************************************************/
    2274             : /*                             ReadFields()                             */
    2275             : /************************************************************************/
    2276             : 
    2277          66 : bool PDS4DelimitedTable::ReadFields(const CPLXMLNode *psParent,
    2278             :                                     const CPLString &osSuffixFieldName)
    2279             : {
    2280         437 :     for (const CPLXMLNode *psIter = psParent->psChild; psIter;
    2281         371 :          psIter = psIter->psNext)
    2282             :     {
    2283         371 :         if (psIter->eType == CXT_Element &&
    2284         371 :             strcmp(psIter->pszValue, "Field_Delimited") == 0)
    2285             :         {
    2286         234 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    2287         234 :             if (!pszName)
    2288             :             {
    2289           0 :                 return false;
    2290             :             }
    2291             :             const char *pszDataType =
    2292         234 :                 CPLGetXMLValue(psIter, "data_type", nullptr);
    2293         234 :             if (!pszDataType)
    2294             :             {
    2295           0 :                 return false;
    2296             :             }
    2297             :             int nMaximumFieldLength =
    2298         234 :                 atoi(CPLGetXMLValue(psIter, "maximum_field_length", "0"));
    2299             : 
    2300         234 :             Field f;
    2301         234 :             f.m_osDataType = pszDataType;
    2302         234 :             f.m_osUnit = CPLGetXMLValue(psIter, "unit", "");
    2303         234 :             f.m_osDescription = CPLGetXMLValue(psIter, "description", "");
    2304             : 
    2305             :             CPLXMLNode *psSpecialConstants = const_cast<CPLXMLNode *>(
    2306         234 :                 CPLGetXMLNode(psIter, "Special_Constants"));
    2307         234 :             if (psSpecialConstants)
    2308             :             {
    2309           0 :                 auto psNext = psSpecialConstants->psNext;
    2310           0 :                 psSpecialConstants->psNext = nullptr;
    2311           0 :                 char *pszXML = CPLSerializeXMLTree(psSpecialConstants);
    2312           0 :                 psSpecialConstants->psNext = psNext;
    2313           0 :                 if (pszXML)
    2314             :                 {
    2315           0 :                     f.m_osSpecialConstantsXML = pszXML;
    2316           0 :                     CPLFree(pszXML);
    2317             :                 }
    2318             :             }
    2319             :             f.m_osMissingConstant = CPLGetXMLValue(
    2320         234 :                 psIter, "Special_Constants.missing_constant", "");
    2321             : 
    2322         234 :             m_aoFields.push_back(f);
    2323             : 
    2324         234 :             OGRFieldSubType eSubType = OFSTNone;
    2325         234 :             bool error = false;
    2326             :             auto eType =
    2327         234 :                 GetFieldTypeFromPDS4DataType(pszDataType, 0, eSubType, error);
    2328         234 :             if (error)
    2329             :             {
    2330           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2331             :                          "Binary fields not allowed");
    2332           0 :                 return false;
    2333             :             }
    2334         422 :             if (STARTS_WITH(f.m_osDataType, "ASCII_") && eType == OFTInteger &&
    2335         424 :                 eSubType == OFSTNone &&
    2336           2 :                 (nMaximumFieldLength == 0 || nMaximumFieldLength >= 10))
    2337             :             {
    2338          42 :                 eType = OFTInteger64;
    2339             :             }
    2340         234 :             OGRFieldDefn oFieldDefn((pszName + osSuffixFieldName).c_str(),
    2341         468 :                                     eType);
    2342         234 :             oFieldDefn.SetSubType(eSubType);
    2343         280 :             if (eType != OFTReal && (STARTS_WITH(f.m_osDataType, "ASCII_") ||
    2344          46 :                                      STARTS_WITH(f.m_osDataType, "UTF_8")))
    2345             :             {
    2346         159 :                 oFieldDefn.SetWidth(nMaximumFieldLength);
    2347             :             }
    2348         468 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    2349             :         }
    2350         137 :         else if (psIter->eType == CXT_Element &&
    2351         137 :                  strcmp(psIter->pszValue, "Group_Field_Delimited") == 0)
    2352             :         {
    2353             :             const char *pszRepetitions =
    2354           1 :                 CPLGetXMLValue(psIter, "repetitions", nullptr);
    2355           1 :             if (!pszRepetitions)
    2356             :             {
    2357           0 :                 return false;
    2358             :             }
    2359           1 :             int nRepetitions = std::min(1000, atoi(pszRepetitions));
    2360           1 :             if (nRepetitions <= 0)
    2361             :             {
    2362           0 :                 return false;
    2363             :             }
    2364           3 :             for (int i = 0; i < nRepetitions; i++)
    2365             :             {
    2366           2 :                 if (!ReadFields(psIter, osSuffixFieldName + "_" +
    2367             :                                             CPLSPrintf("%d", i + 1)))
    2368             :                 {
    2369           0 :                     return false;
    2370             :                 }
    2371             :             }
    2372             :         }
    2373             :     }
    2374          66 :     return true;
    2375             : }
    2376             : 
    2377             : /************************************************************************/
    2378             : /*                    RefreshFileAreaObservational()                    */
    2379             : /************************************************************************/
    2380             : 
    2381          55 : void PDS4DelimitedTable::RefreshFileAreaObservational(CPLXMLNode *psFAO)
    2382             : {
    2383         110 :     CPLString osPrefix;
    2384          55 :     if (STARTS_WITH(psFAO->pszValue, "pds:"))
    2385           0 :         osPrefix = "pds:";
    2386             : 
    2387         110 :     CPLString osDescription;
    2388          55 :     CPLXMLNode *psTable = RefreshFileAreaObservationalBeginningCommon(
    2389             :         psFAO, osPrefix, "Table_Delimited", osDescription);
    2390             : 
    2391          55 :     CPLCreateXMLElementAndValue(
    2392         110 :         psTable, (osPrefix + "parsing_standard_id").c_str(), "PDS DSV 1");
    2393             : 
    2394          55 :     CPLCreateXMLElementAndValue(psTable, (osPrefix + "records").c_str(),
    2395             :                                 CPLSPrintf(CPL_FRMT_GIB, m_nFeatureCount));
    2396          55 :     if (!osDescription.empty())
    2397           0 :         CPLCreateXMLElementAndValue(psTable, (osPrefix + "description").c_str(),
    2398             :                                     osDescription);
    2399             : 
    2400          55 :     if (m_osLineEnding == "\r\n")
    2401             :     {
    2402          54 :         CPLCreateXMLElementAndValue(psTable,
    2403         108 :                                     (osPrefix + "record_delimiter").c_str(),
    2404             :                                     "Carriage-Return Line-Feed");
    2405             :     }
    2406           1 :     else if (m_osLineEnding == "\n")
    2407             :     {
    2408           1 :         CPLCreateXMLElementAndValue(
    2409           2 :             psTable, (osPrefix + "record_delimiter").c_str(), "Line-Feed");
    2410             :     }
    2411             : 
    2412          55 :     CPLCreateXMLElementAndValue(psTable, (osPrefix + "field_delimiter").c_str(),
    2413          55 :                                 m_chFieldDelimiter == '\t'  ? "Horizontal Tab"
    2414         110 :                                 : m_chFieldDelimiter == ';' ? "Semicolon"
    2415          55 :                                 : m_chFieldDelimiter == '|' ? "Vertical Bar"
    2416             :                                                             : "Comma");
    2417             : 
    2418             :     // Write Record_Delimited
    2419          55 :     CPLXMLNode *psRecord = CPLCreateXMLNode(
    2420         110 :         psTable, CXT_Element, (osPrefix + "Record_Delimited").c_str());
    2421             : 
    2422         110 :     CPLCreateXMLElementAndValue(
    2423         110 :         psRecord, (osPrefix + "fields").c_str(),
    2424          55 :         CPLSPrintf("%d", static_cast<int>(m_aoFields.size())));
    2425             : 
    2426          55 :     CPLXMLNode *psLastChild = CPLCreateXMLElementAndValue(
    2427         110 :         psRecord, (osPrefix + "groups").c_str(), "0");
    2428             : 
    2429          55 :     CPLAssert(static_cast<int>(m_aoFields.size()) ==
    2430             :               m_poRawFeatureDefn->GetFieldCount());
    2431             : 
    2432         110 :     const auto osPrefixedFieldDelimited(osPrefix + "Field_Delimited");
    2433         110 :     const auto osPrefixedName(osPrefix + "name");
    2434         110 :     const auto osPrefixedFieldNumber(osPrefix + "field_number");
    2435         110 :     const auto osPrefixedFieldData(osPrefix + "data_type");
    2436         110 :     const auto osPrefixMaxFieldLength(osPrefix + "maximum_field_length");
    2437         110 :     const auto osPrefixedUnit(osPrefix + "unit");
    2438         110 :     const auto osPrefixedDescription(osPrefix + "description");
    2439          55 :     CPLAssert(psLastChild->psNext == nullptr);
    2440         224 :     for (int i = 0; i < static_cast<int>(m_aoFields.size()); i++)
    2441             :     {
    2442         169 :         const auto &f = m_aoFields[i];
    2443             : 
    2444         169 :         CPLXMLNode *psField = CPLCreateXMLNode(
    2445             :             nullptr, CXT_Element, osPrefixedFieldDelimited.c_str());
    2446         169 :         psLastChild->psNext = psField;
    2447         169 :         psLastChild = psField;
    2448             : 
    2449         169 :         CPLCreateXMLElementAndValue(
    2450             :             psField, osPrefixedName.c_str(),
    2451         169 :             m_poRawFeatureDefn->GetFieldDefn(i)->GetNameRef());
    2452             : 
    2453         169 :         CPLCreateXMLElementAndValue(psField, osPrefixedFieldNumber.c_str(),
    2454             :                                     CPLSPrintf("%d", i + 1));
    2455             : 
    2456         169 :         CPLCreateXMLElementAndValue(psField, osPrefixedFieldData.c_str(),
    2457             :                                     f.m_osDataType.c_str());
    2458             : 
    2459         169 :         int nWidth = m_poRawFeatureDefn->GetFieldDefn(i)->GetWidth();
    2460         169 :         if (nWidth > 0)
    2461             :         {
    2462           1 :             auto psfield_length = CPLCreateXMLElementAndValue(
    2463             :                 psField, osPrefixMaxFieldLength.c_str(),
    2464             :                 CPLSPrintf("%d", nWidth));
    2465           1 :             CPLAddXMLAttributeAndValue(psfield_length, "unit", "byte");
    2466             :         }
    2467             : 
    2468         169 :         if (!f.m_osUnit.empty())
    2469             :         {
    2470           0 :             CPLCreateXMLElementAndValue(psField, osPrefixedUnit.c_str(),
    2471           0 :                                         m_aoFields[i].m_osUnit.c_str());
    2472             :         }
    2473             : 
    2474         169 :         if (!f.m_osDescription.empty())
    2475             :         {
    2476           0 :             CPLCreateXMLElementAndValue(psField, osPrefixedDescription.c_str(),
    2477           0 :                                         m_aoFields[i].m_osDescription.c_str());
    2478             :         }
    2479             : 
    2480         169 :         if (!f.m_osSpecialConstantsXML.empty())
    2481             :         {
    2482             :             auto psSpecialConstants =
    2483           0 :                 CPLParseXMLString(f.m_osSpecialConstantsXML);
    2484           0 :             if (psSpecialConstants)
    2485             :             {
    2486           0 :                 CPLAddXMLChild(psField, psSpecialConstants);
    2487             :             }
    2488             :         }
    2489             :     }
    2490          55 : }
    2491             : 
    2492             : /************************************************************************/
    2493             : /*                            GetFileList()                             */
    2494             : /************************************************************************/
    2495             : 
    2496          53 : char **PDS4DelimitedTable::GetFileList() const
    2497             : {
    2498          53 :     auto papszFileList = PDS4TableBaseLayer::GetFileList();
    2499          53 :     CPLString osVRTFilename = CPLResetExtensionSafe(m_osFilename, "vrt");
    2500             :     VSIStatBufL sStat;
    2501          53 :     if (VSIStatL(osVRTFilename, &sStat) == 0)
    2502             :     {
    2503          52 :         papszFileList = CSLAddString(papszFileList, osVRTFilename);
    2504             :     }
    2505         106 :     return papszFileList;
    2506             : }
    2507             : 
    2508             : /************************************************************************/
    2509             : /*                         InitializeNewLayer()                         */
    2510             : /************************************************************************/
    2511             : 
    2512          56 : bool PDS4DelimitedTable::InitializeNewLayer(const OGRSpatialReference *poSRS,
    2513             :                                             bool bForceGeographic,
    2514             :                                             OGRwkbGeometryType eGType,
    2515             :                                             const char *const *papszOptions)
    2516             : {
    2517          56 :     CPLAssert(m_fp == nullptr);
    2518          56 :     m_fp = VSIFOpenL(m_osFilename, "wb+");
    2519          56 :     if (!m_fp)
    2520             :     {
    2521           1 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
    2522             :                  m_osFilename.c_str());
    2523           1 :         return false;
    2524             :     }
    2525          55 :     m_aosLCO.Assign(CSLDuplicate(papszOptions));
    2526          55 :     m_bCreation = true;
    2527             : 
    2528             :     // For testing purposes
    2529          55 :     m_chFieldDelimiter = CPLGetConfigOption("OGR_PDS4_FIELD_DELIMITER", ",")[0];
    2530             : 
    2531             :     const char *pszGeomColumns =
    2532          55 :         CSLFetchNameValueDef(papszOptions, "GEOM_COLUMNS", "AUTO");
    2533          55 :     if ((EQUAL(pszGeomColumns, "AUTO") && wkbFlatten(eGType) == wkbPoint &&
    2534         116 :          (bForceGeographic || (poSRS && poSRS->IsGeographic()))) ||
    2535          55 :         (EQUAL(pszGeomColumns, "LONG_LAT") && eGType != wkbNone))
    2536             :     {
    2537             :         {
    2538             :             OGRFieldDefn oFieldDefn(
    2539           0 :                 CSLFetchNameValueDef(papszOptions, "LAT", "Latitude"), OFTReal);
    2540           0 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    2541           0 :             m_iLatField = m_poRawFeatureDefn->GetFieldCount() - 1;
    2542           0 :             Field f;
    2543           0 :             f.m_osDataType = "ASCII_Real";
    2544           0 :             m_aoFields.push_back(std::move(f));
    2545             :         }
    2546             :         {
    2547             :             OGRFieldDefn oFieldDefn(
    2548             :                 CSLFetchNameValueDef(papszOptions, "LONG", "Longitude"),
    2549           0 :                 OFTReal);
    2550           0 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    2551           0 :             m_iLongField = m_poRawFeatureDefn->GetFieldCount() - 1;
    2552           0 :             Field f;
    2553           0 :             f.m_osDataType = "ASCII_Real";
    2554           0 :             m_aoFields.push_back(std::move(f));
    2555             :         }
    2556           0 :         if (eGType == wkbPoint25D)
    2557             :         {
    2558             :             OGRFieldDefn oFieldDefn(
    2559           0 :                 CSLFetchNameValueDef(papszOptions, "ALT", "Altitude"), OFTReal);
    2560           0 :             m_poRawFeatureDefn->AddFieldDefn(&oFieldDefn);
    2561           0 :             m_iAltField = m_poRawFeatureDefn->GetFieldCount() - 1;
    2562           0 :             Field f;
    2563           0 :             f.m_osDataType = "ASCII_Real";
    2564           0 :             m_aoFields.push_back(std::move(f));
    2565             :         }
    2566             :     }
    2567          55 :     else if (eGType != wkbNone &&
    2568          51 :              (EQUAL(pszGeomColumns, "AUTO") || EQUAL(pszGeomColumns, "WKT")))
    2569             :     {
    2570          51 :         m_bAddWKTColumnPending = true;
    2571             :     }
    2572             : 
    2573          55 :     if (eGType != wkbNone)
    2574             :     {
    2575          51 :         m_poRawFeatureDefn->SetGeomType(eGType);
    2576             : 
    2577          51 :         m_poFeatureDefn->SetGeomType(eGType);
    2578          51 :         if (poSRS)
    2579             :         {
    2580           2 :             auto poSRSClone = poSRS->Clone();
    2581           2 :             poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2582           2 :             m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
    2583           2 :             poSRSClone->Release();
    2584             :         }
    2585             :     }
    2586             : 
    2587          55 :     ParseLineEndingOption(papszOptions);
    2588             : 
    2589          55 :     m_nFeatureCount = 0;
    2590          55 :     MarkHeaderDirty();
    2591          55 :     return true;
    2592             : }
    2593             : 
    2594             : /************************************************************************/
    2595             : /* ==================================================================== */
    2596             : /*                           PDS4EditableSynchronizer                   */
    2597             : /* ==================================================================== */
    2598             : /************************************************************************/
    2599             : 
    2600             : template <class T>
    2601             : class PDS4EditableSynchronizer final : public IOGREditableLayerSynchronizer
    2602             : {
    2603             :   public:
    2604          74 :     PDS4EditableSynchronizer() = default;
    2605             : 
    2606             :     OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
    2607             :                               OGRLayer **ppoDecoratedLayer) override;
    2608             : };
    2609             : 
    2610             : template <class T>
    2611             : OGRErr
    2612           3 : PDS4EditableSynchronizer<T>::EditableSyncToDisk(OGRLayer *poEditableLayer,
    2613             :                                                 OGRLayer **ppoDecoratedLayer)
    2614             : {
    2615           3 :     auto poOriLayer = cpl::down_cast<T *>(*ppoDecoratedLayer);
    2616             : 
    2617           6 :     CPLString osTmpFilename(poOriLayer->m_osFilename + ".tmp");
    2618           3 :     auto poNewLayer = poOriLayer->NewLayer(
    2619             :         poOriLayer->m_poDS, poOriLayer->GetName(), osTmpFilename);
    2620           6 :     CPLStringList aosLCO(poOriLayer->m_aosLCO);
    2621           3 :     if (poOriLayer->m_iLatField >= 0)
    2622             :     {
    2623           2 :         aosLCO.SetNameValue("LAT", poOriLayer->m_poRawFeatureDefn
    2624             :                                        ->GetFieldDefn(poOriLayer->m_iLatField)
    2625             :                                        ->GetNameRef());
    2626             :     }
    2627           3 :     if (poOriLayer->m_iLongField >= 0)
    2628             :     {
    2629           2 :         aosLCO.SetNameValue("LONG", poOriLayer->m_poRawFeatureDefn
    2630             :                                         ->GetFieldDefn(poOriLayer->m_iLongField)
    2631             :                                         ->GetNameRef());
    2632             :     }
    2633           3 :     if (poOriLayer->m_iAltField >= 0)
    2634             :     {
    2635           2 :         aosLCO.SetNameValue("ALT", poOriLayer->m_poRawFeatureDefn
    2636             :                                        ->GetFieldDefn(poOriLayer->m_iAltField)
    2637             :                                        ->GetNameRef());
    2638             :     }
    2639           6 :     if (!poNewLayer->InitializeNewLayer(
    2640           3 :             poOriLayer->GetSpatialRef(), poOriLayer->m_iLatField >= 0,
    2641           3 :             poOriLayer->GetGeomType(), aosLCO.List()))
    2642             :     {
    2643           0 :         delete poNewLayer;
    2644           0 :         VSIUnlink(osTmpFilename);
    2645           0 :         return OGRERR_FAILURE;
    2646             :     }
    2647             : 
    2648             :     const auto copyField =
    2649          94 :         [](typename T::Field &oDst, const typename T::Field &oSrc)
    2650             :     {
    2651          47 :         oDst.m_osDescription = oSrc.m_osDescription;
    2652          47 :         oDst.m_osUnit = oSrc.m_osUnit;
    2653          47 :         oDst.m_osSpecialConstantsXML = oSrc.m_osSpecialConstantsXML;
    2654             :     };
    2655             : 
    2656           3 :     if (poNewLayer->m_iLatField >= 0)
    2657             :     {
    2658           2 :         copyField(poNewLayer->m_aoFields[poNewLayer->m_iLatField],
    2659           2 :                   poOriLayer->m_aoFields[poOriLayer->m_iLatField]);
    2660             :     }
    2661           3 :     if (poNewLayer->m_iLongField >= 0)
    2662             :     {
    2663           2 :         copyField(poNewLayer->m_aoFields[poNewLayer->m_iLongField],
    2664           2 :                   poOriLayer->m_aoFields[poOriLayer->m_iLongField]);
    2665             :     }
    2666           3 :     if (poNewLayer->m_iAltField >= 0)
    2667             :     {
    2668           2 :         copyField(poNewLayer->m_aoFields[poNewLayer->m_iAltField],
    2669           2 :                   poOriLayer->m_aoFields[poOriLayer->m_iAltField]);
    2670             :     }
    2671             : 
    2672           3 :     OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
    2673          44 :     for (int i = 0; i < poEditableFDefn->GetFieldCount(); i++)
    2674             :     {
    2675          41 :         auto poFieldDefn = poEditableFDefn->GetFieldDefn(i);
    2676          41 :         poNewLayer->CreateField(poFieldDefn, false);
    2677          41 :         int idx = poOriLayer->m_poRawFeatureDefn->GetFieldIndex(
    2678             :             poFieldDefn->GetNameRef());
    2679          41 :         if (idx >= 0)
    2680             :         {
    2681          41 :             copyField(poNewLayer->m_aoFields.back(),
    2682          41 :                       poOriLayer->m_aoFields[idx]);
    2683          82 :             OGRFieldDefn *poOriFieldDefn =
    2684          41 :                 poOriLayer->m_poRawFeatureDefn->GetFieldDefn(idx);
    2685          41 :             if (poFieldDefn->GetType() == poOriFieldDefn->GetType())
    2686             :             {
    2687          41 :                 poNewLayer->m_aoFields.back().m_osDataType =
    2688          41 :                     poOriLayer->m_aoFields[idx].m_osDataType;
    2689             :             }
    2690             :         }
    2691             :     }
    2692             : 
    2693           3 :     poEditableLayer->ResetReading();
    2694             : 
    2695             :     // Disable all filters.
    2696           3 :     const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
    2697           3 :     char *pszQueryStringBak =
    2698           2 :         pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
    2699           3 :     poEditableLayer->SetAttributeFilter(nullptr);
    2700             : 
    2701           3 :     const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
    2702           3 :     OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
    2703           3 :     if (poFilterGeomBak)
    2704           0 :         poFilterGeomBak = poFilterGeomBak->clone();
    2705           3 :     poEditableLayer->SetSpatialFilter(nullptr);
    2706             : 
    2707           6 :     auto aoMapSrcToTargetIdx = poNewLayer->GetLayerDefn()->ComputeMapForSetFrom(
    2708           3 :         poEditableLayer->GetLayerDefn(), true);
    2709           3 :     aoMapSrcToTargetIdx.push_back(
    2710           3 :         -1);  // add dummy entry to be sure that .data() is valid
    2711             : 
    2712           3 :     OGRErr eErr = OGRERR_NONE;
    2713          45 :     for (auto &&poFeature : poEditableLayer)
    2714             :     {
    2715          21 :         OGRFeature *poNewFeature = new OGRFeature(poNewLayer->GetLayerDefn());
    2716          21 :         poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
    2717             :                               true);
    2718          21 :         eErr = poNewLayer->CreateFeature(poNewFeature);
    2719          21 :         delete poNewFeature;
    2720          21 :         if (eErr != OGRERR_NONE)
    2721             :         {
    2722           0 :             break;
    2723             :         }
    2724             :     }
    2725             : 
    2726             :     // Restore filters.
    2727           3 :     poEditableLayer->SetAttributeFilter(pszQueryStringBak);
    2728           3 :     CPLFree(pszQueryStringBak);
    2729           3 :     poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
    2730           3 :     delete poFilterGeomBak;
    2731             : 
    2732           6 :     if (eErr != OGRERR_NONE ||
    2733           3 :         !poNewLayer->RenameFileTo(poOriLayer->GetFileName()))
    2734             :     {
    2735           0 :         delete poNewLayer;
    2736           0 :         VSIUnlink(osTmpFilename);
    2737           0 :         return OGRERR_FAILURE;
    2738             :     }
    2739             : 
    2740           3 :     delete poOriLayer;
    2741           3 :     *ppoDecoratedLayer = poNewLayer;
    2742             : 
    2743           3 :     return OGRERR_NONE;
    2744             : }
    2745             : 
    2746             : /************************************************************************/
    2747             : /* ==================================================================== */
    2748             : /*                         PDS4EditableLayer                            */
    2749             : /* ==================================================================== */
    2750             : /************************************************************************/
    2751             : 
    2752          18 : PDS4EditableLayer::PDS4EditableLayer(
    2753          18 :     std::unique_ptr<PDS4FixedWidthTable> poBaseLayer)
    2754             :     : OGREditableLayer(
    2755          18 :           poBaseLayer.release(), true,
    2756          18 :           std::make_unique<PDS4EditableSynchronizer<PDS4FixedWidthTable>>()
    2757          18 :               .release(),
    2758          54 :           true)
    2759             : {
    2760          18 : }
    2761             : 
    2762          56 : PDS4EditableLayer::PDS4EditableLayer(
    2763          56 :     std::unique_ptr<PDS4DelimitedTable> poBaseLayer)
    2764             :     : OGREditableLayer(
    2765          56 :           poBaseLayer.release(), true,
    2766          56 :           std::make_unique<PDS4EditableSynchronizer<PDS4DelimitedTable>>()
    2767          56 :               .release(),
    2768         168 :           true)
    2769             : {
    2770          56 : }
    2771             : 
    2772             : PDS4EditableLayer::~PDS4EditableLayer() = default;
    2773             : 
    2774             : /************************************************************************/
    2775             : /*                            GetBaseLayer()                            */
    2776             : /************************************************************************/
    2777             : 
    2778         505 : PDS4TableBaseLayer *PDS4EditableLayer::GetBaseLayer() const
    2779             : {
    2780         505 :     return cpl::down_cast<PDS4TableBaseLayer *>(
    2781         505 :         OGREditableLayer::GetBaseLayer());
    2782             : }
    2783             : 
    2784             : /************************************************************************/
    2785             : /*                           SetSpatialRef()                            */
    2786             : /************************************************************************/
    2787             : 
    2788           1 : void PDS4EditableLayer::SetSpatialRef(OGRSpatialReference *poSRS)
    2789             : {
    2790           1 :     if (GetGeomType() != wkbNone)
    2791             :     {
    2792           1 :         GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    2793           1 :         GetBaseLayer()->GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(
    2794             :             poSRS);
    2795             :     }
    2796           1 : }

Generated by: LCOV version 1.14