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

Generated by: LCOV version 1.14