LCOV - code coverage report
Current view: top level - frmts/pds - pds4vector.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1231 1411 87.2 %
Date: 2024-11-21 22:18:42 Functions: 55 56 98.2 %

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

Generated by: LCOV version 1.14