LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/vdv - ogrvdvdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1060 1109 95.6 %
Date: 2025-02-20 10:14:44 Functions: 45 45 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VDV Translator
       4             :  * Purpose:  Implements OGRVDVFDriver.
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_vdv.h"
      14             : #include "cpl_conv.h"
      15             : #include "cpl_time.h"
      16             : #include <map>
      17             : 
      18             : #ifdef EMBED_RESOURCE_FILES
      19             : #include "embedded_resources.h"
      20             : #endif
      21             : 
      22             : #ifndef STARTS_WITH_CI
      23             : #define STARTS_WITH(a, b) (strncmp(a, b, strlen(b)) == 0)
      24             : #define STARTS_WITH_CI(a, b) EQUALN(a, b, strlen(b))
      25             : #endif
      26             : 
      27             : typedef enum
      28             : {
      29             :     LAYER_OTHER,
      30             :     LAYER_NODE,
      31             :     LAYER_LINK,
      32             :     LAYER_LINKCOORDINATE
      33             : } IDFLayerType;
      34             : 
      35             : /************************************************************************/
      36             : /*                          OGRVDVParseAtrFrm()                         */
      37             : /************************************************************************/
      38             : 
      39         106 : static void OGRVDVParseAtrFrm(OGRLayer *poLayer, OGRFeatureDefn *poFeatureDefn,
      40             :                               char **papszAtr, char **papszFrm)
      41             : {
      42         507 :     for (int i = 0; papszAtr[i]; i++)
      43             :     {
      44         401 :         OGRFieldType eType = OFTString;
      45         401 :         int nWidth = 0;
      46         401 :         OGRFieldSubType eSubType = OFSTNone;
      47         401 :         if (STARTS_WITH_CI(papszFrm[i], "decimal"))
      48             :         {
      49          90 :             if (papszFrm[i][strlen("decimal")] == '(')
      50             :             {
      51          90 :                 if (strchr(papszFrm[i], ',') &&
      52          34 :                     atoi(strchr(papszFrm[i], ',') + 1) > 0)
      53             :                 {
      54          34 :                     eType = OFTReal;
      55             :                 }
      56             :                 else
      57             :                 {
      58          56 :                     nWidth = atoi(papszFrm[i] + strlen("decimal") + 1);
      59          56 :                     if (nWidth >= 10)
      60          48 :                         eType = OFTInteger64;
      61             :                     else
      62           8 :                         eType = OFTInteger;
      63             :                 }
      64             :             }
      65             :             else
      66           0 :                 eType = OFTInteger;
      67             :         }
      68         311 :         else if (STARTS_WITH_CI(papszFrm[i], "num"))
      69             :         {
      70         119 :             if (papszFrm[i][strlen("num")] == '[')
      71             :             {
      72         119 :                 if (strchr(papszFrm[i], '.') &&
      73         119 :                     atoi(strchr(papszFrm[i], '.') + 1) > 0)
      74             :                 {
      75           0 :                     eType = OFTReal;
      76             :                 }
      77             :                 else
      78             :                 {
      79         119 :                     nWidth = atoi(papszFrm[i] + strlen("num") + 1);
      80         119 :                     if (nWidth < 0 || nWidth >= 100)
      81             :                     {
      82           0 :                         nWidth = 0;
      83           0 :                         eType = OFTInteger;
      84             :                     }
      85             :                     else
      86             :                     {
      87         119 :                         nWidth += 1; /* VDV-451 width is without sign */
      88         119 :                         if (nWidth >= 10)
      89          74 :                             eType = OFTInteger64;
      90             :                         else
      91          45 :                             eType = OFTInteger;
      92             :                     }
      93             :                 }
      94             :             }
      95             :             else
      96           0 :                 eType = OFTInteger;
      97             :         }
      98         192 :         else if (STARTS_WITH_CI(papszFrm[i], "char"))
      99             :         {
     100         151 :             if (papszFrm[i][strlen("char")] == '[')
     101             :             {
     102         151 :                 nWidth = atoi(papszFrm[i] + strlen("char") + 1);
     103         151 :                 if (nWidth < 0)
     104           0 :                     nWidth = 0;
     105             :             }
     106             :         }
     107          41 :         else if (STARTS_WITH_CI(papszFrm[i], "boolean"))
     108             :         {
     109          17 :             eType = OFTInteger;
     110          17 :             eSubType = OFSTBoolean;
     111             :         }
     112         802 :         OGRFieldDefn oFieldDefn(papszAtr[i], eType);
     113         401 :         oFieldDefn.SetSubType(eSubType);
     114         401 :         oFieldDefn.SetWidth(nWidth);
     115         401 :         if (poLayer)
     116         114 :             poLayer->CreateField(&oFieldDefn);
     117         287 :         else if (poFeatureDefn)
     118         287 :             poFeatureDefn->AddFieldDefn(&oFieldDefn);
     119             :         else
     120             :         {
     121           0 :             CPLAssert(false);
     122             :         }
     123             :     }
     124         106 : }
     125             : 
     126             : /************************************************************************/
     127             : /*                           OGRIDFDataSource()                         */
     128             : /************************************************************************/
     129             : 
     130           8 : OGRIDFDataSource::OGRIDFDataSource(const char *pszFilename, VSILFILE *fpLIn)
     131             :     : m_osFilename(pszFilename), m_fpL(fpLIn), m_bHasParsed(false),
     132           8 :       m_poTmpDS(nullptr)
     133             : {
     134           8 : }
     135             : 
     136             : /************************************************************************/
     137             : /*                          ~OGRIDFDataSource()                         */
     138             : /************************************************************************/
     139             : 
     140          16 : OGRIDFDataSource::~OGRIDFDataSource()
     141             : {
     142          16 :     CPLString osTmpFilename;
     143           8 :     if (m_bDestroyTmpDS && m_poTmpDS)
     144             :     {
     145           0 :         osTmpFilename = m_poTmpDS->GetDescription();
     146             :     }
     147           8 :     delete m_poTmpDS;
     148           8 :     if (m_bDestroyTmpDS)
     149             :     {
     150           0 :         VSIUnlink(osTmpFilename);
     151             :     }
     152           8 :     if (m_fpL)
     153             :     {
     154           8 :         VSIFCloseL(m_fpL);
     155             :     }
     156          16 : }
     157             : 
     158             : /************************************************************************/
     159             : /*                                Parse()                               */
     160             : /************************************************************************/
     161             : 
     162           8 : void OGRIDFDataSource::Parse()
     163             : {
     164           8 :     m_bHasParsed = true;
     165             : 
     166             :     GDALDriver *poMEMDriver =
     167           8 :         reinterpret_cast<GDALDriver *>(GDALGetDriverByName("MEMORY"));
     168           8 :     if (poMEMDriver == nullptr)
     169           0 :         return;
     170             : 
     171             :     VSIStatBufL sStatBuf;
     172           8 :     bool bGPKG = false;
     173           8 :     vsi_l_offset nFileSize = 0;
     174           8 :     bool bSpatialIndex = false;
     175          16 :     if (VSIStatL(m_osFilename, &sStatBuf) == 0 &&
     176           8 :         sStatBuf.st_size > CPLAtoGIntBig(CPLGetConfigOption(
     177             :                                "OGR_IDF_TEMP_DB_THRESHOLD", "100000000")))
     178             :     {
     179           1 :         nFileSize = sStatBuf.st_size;
     180             : 
     181             :         GDALDriver *poGPKGDriver =
     182           1 :             reinterpret_cast<GDALDriver *>(GDALGetDriverByName("GPKG"));
     183           1 :         if (poGPKGDriver)
     184             :         {
     185           2 :             CPLString osTmpFilename(m_osFilename + "_tmp.gpkg");
     186           1 :             VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
     187           1 :             if (fp)
     188             :             {
     189           1 :                 VSIFCloseL(fp);
     190             :             }
     191             :             else
     192             :             {
     193           0 :                 osTmpFilename = CPLGenerateTempFilenameSafe(
     194           0 :                     CPLGetBasenameSafe(m_osFilename).c_str());
     195           0 :                 osTmpFilename += ".gpkg";
     196             :             }
     197           1 :             VSIUnlink(osTmpFilename);
     198             :             {
     199             :                 CPLConfigOptionSetter oSetter1("OGR_SQLITE_JOURNAL", "OFF",
     200           2 :                                                false);
     201             :                 // For use of OGR VSI-based SQLite3 VFS implementation, as
     202             :                 // the regular SQLite3 implementation has some issues to deal
     203             :                 // with a file that is deleted after having been created.
     204             :                 // For example on MacOS Big Sur system's sqlite 3.32.3
     205             :                 // when chaining ogr_sqlite.py and ogr_vdv.py, or in Vagrant
     206             :                 // Ubuntu 22.04 environment with sqlite 3.37.2
     207             :                 CPLConfigOptionSetter oSetter2("SQLITE_USE_OGR_VFS", "YES",
     208           1 :                                                false);
     209           1 :                 m_poTmpDS = poGPKGDriver->Create(osTmpFilename, 0, 0, 0,
     210             :                                                  GDT_Unknown, nullptr);
     211             :             }
     212           1 :             bGPKG = m_poTmpDS != nullptr;
     213           1 :             m_bDestroyTmpDS = CPLTestBool(CPLGetConfigOption(
     214           2 :                                   "OGR_IDF_DELETE_TEMP_DB", "YES")) &&
     215           1 :                               m_poTmpDS != nullptr;
     216           1 :             if (m_bDestroyTmpDS)
     217             :             {
     218           1 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
     219           1 :                 m_bDestroyTmpDS = VSIUnlink(osTmpFilename) != 0;
     220           1 :                 CPLPopErrorHandler();
     221             :             }
     222             :             else
     223             :             {
     224           0 :                 bSpatialIndex = true;
     225             :             }
     226             :         }
     227             :     }
     228             : 
     229           8 :     bool bIsMEMLayer = false;
     230           8 :     if (m_poTmpDS == nullptr)
     231             :     {
     232           7 :         bIsMEMLayer = true;
     233           7 :         m_poTmpDS = poMEMDriver->Create("", 0, 0, 0, GDT_Unknown, nullptr);
     234             :     }
     235             : 
     236           8 :     m_poTmpDS->StartTransaction();
     237             : 
     238           8 :     OGRLayer *poCurLayer = nullptr;
     239             : 
     240             :     struct Point
     241             :     {
     242             :         double x;
     243             :         double y;
     244             :         double z;
     245             : 
     246          32 :         explicit Point(double xIn = 0, double yIn = 0, double zIn = 0)
     247          32 :             : x(xIn), y(yIn), z(zIn)
     248             :         {
     249          32 :         }
     250             :     };
     251             : 
     252          16 :     std::map<GIntBig, Point> oMapNode;  // map from NODE_ID to Point
     253             :     std::map<GIntBig, OGRLineString *>
     254          16 :         oMapLinkCoordinate;  // map from LINK_ID to OGRLineString*
     255          16 :     CPLString osTablename, osAtr, osFrm;
     256           8 :     int iX = -1, iY = -1, iZ = -1;
     257           8 :     bool bAdvertiseUTF8 = false;
     258           8 :     bool bRecodeFromLatin1 = false;
     259           8 :     int iNodeID = -1;
     260           8 :     int iLinkID = -1;
     261           8 :     int iFromNode = -1;
     262           8 :     int iToNode = -1;
     263           8 :     IDFLayerType eLayerType = LAYER_OTHER;
     264             : 
     265             :     // We assume that layers are in the order Node, Link, LinkCoordinate
     266             : 
     267           8 :     GUIntBig nLineCount = 0;
     268             :     while (true)
     269             :     {
     270         312 :         if (nFileSize)
     271             :         {
     272          39 :             ++nLineCount;
     273          39 :             if ((nLineCount % 32768) == 0)
     274             :             {
     275           0 :                 const vsi_l_offset nPos = VSIFTellL(m_fpL);
     276           0 :                 CPLDebug("IDF", "Reading progress: %.2f %%",
     277           0 :                          100.0 * nPos / nFileSize);
     278             :             }
     279             :         }
     280             : 
     281         312 :         const char *pszLine = CPLReadLineL(m_fpL);
     282         312 :         if (pszLine == nullptr)
     283           8 :             break;
     284             : 
     285         304 :         if (strcmp(pszLine, "chs;ISO_LATIN_1") == 0)
     286             :         {
     287           8 :             bAdvertiseUTF8 = true;
     288           8 :             bRecodeFromLatin1 = true;
     289             :         }
     290         296 :         else if (STARTS_WITH(pszLine, "tbl;"))
     291             :         {
     292          32 :             poCurLayer = nullptr;
     293          32 :             osTablename = pszLine + 4;
     294          32 :             osAtr = "";
     295          32 :             osFrm = "";
     296          32 :             iX = iY = iNodeID = iLinkID = iFromNode = iToNode = -1;
     297          32 :             eLayerType = LAYER_OTHER;
     298             :         }
     299         264 :         else if (STARTS_WITH(pszLine, "atr;"))
     300             :         {
     301          32 :             osAtr = pszLine + 4;
     302          32 :             osAtr.Trim();
     303             :         }
     304         232 :         else if (STARTS_WITH(pszLine, "frm;"))
     305             :         {
     306          32 :             osFrm = pszLine + 4;
     307          32 :             osFrm.Trim();
     308             :         }
     309         200 :         else if (STARTS_WITH(pszLine, "rec;"))
     310             :         {
     311          80 :             if (poCurLayer == nullptr)
     312             :             {
     313          32 :                 char **papszAtr = CSLTokenizeString2(osAtr, ";",
     314             :                                                      CSLT_ALLOWEMPTYTOKENS |
     315             :                                                          CSLT_STRIPLEADSPACES |
     316             :                                                          CSLT_STRIPENDSPACES);
     317          32 :                 char **papszFrm = CSLTokenizeString2(osFrm, ";",
     318             :                                                      CSLT_ALLOWEMPTYTOKENS |
     319             :                                                          CSLT_STRIPLEADSPACES |
     320             :                                                          CSLT_STRIPENDSPACES);
     321          32 :                 char *apszOptions[2] = {nullptr, nullptr};
     322          32 :                 if (bAdvertiseUTF8 && !bGPKG)
     323          28 :                     apszOptions[0] = (char *)"ADVERTIZE_UTF8=YES";
     324           4 :                 else if (bGPKG && !bSpatialIndex)
     325           4 :                     apszOptions[0] = (char *)"SPATIAL_INDEX=NO";
     326             : 
     327          32 :                 if (EQUAL(osTablename, "Node") &&
     328          40 :                     (iX = CSLFindString(papszAtr, "X")) >= 0 &&
     329           8 :                     (iY = CSLFindString(papszAtr, "Y")) >= 0)
     330             :                 {
     331           8 :                     iZ = CSLFindString(papszAtr, "Z");
     332           8 :                     eLayerType = LAYER_NODE;
     333           8 :                     iNodeID = CSLFindString(papszAtr, "NODE_ID");
     334             :                     OGRSpatialReference *poSRS =
     335           8 :                         new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     336           8 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     337           8 :                     poCurLayer = m_poTmpDS->CreateLayer(
     338             :                         osTablename, poSRS, iZ < 0 ? wkbPoint : wkbPoint25D,
     339             :                         apszOptions);
     340           8 :                     poSRS->Release();
     341             :                 }
     342          24 :                 else if (EQUAL(osTablename, "Link") &&
     343           8 :                          (iLinkID = CSLFindString(papszAtr, "LINK_ID")) >= 0 &&
     344           8 :                          ((iFromNode = CSLFindString(papszAtr, "FROM_NODE")) >=
     345          32 :                           0) &&
     346           8 :                          ((iToNode = CSLFindString(papszAtr, "TO_NODE")) >= 0))
     347             :                 {
     348           8 :                     eLayerType = LAYER_LINK;
     349             :                     OGRSpatialReference *poSRS =
     350           8 :                         new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     351           8 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     352           8 :                     poCurLayer = m_poTmpDS->CreateLayer(
     353             :                         osTablename, poSRS,
     354             :                         iZ < 0 ? wkbLineString : wkbLineString25D, apszOptions);
     355           8 :                     poSRS->Release();
     356             :                 }
     357          16 :                 else if (EQUAL(osTablename, "LinkCoordinate") &&
     358           8 :                          (iLinkID = CSLFindString(papszAtr, "LINK_ID")) >= 0 &&
     359           8 :                          CSLFindString(papszAtr, "COUNT") >= 0 &&
     360          32 :                          (iX = CSLFindString(papszAtr, "X")) >= 0 &&
     361           8 :                          (iY = CSLFindString(papszAtr, "Y")) >= 0)
     362             :                 {
     363           8 :                     iZ = CSLFindString(papszAtr, "Z");
     364           8 :                     eLayerType = LAYER_LINKCOORDINATE;
     365             :                     OGRSpatialReference *poSRS =
     366           8 :                         new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     367           8 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     368           8 :                     poCurLayer = m_poTmpDS->CreateLayer(
     369             :                         osTablename, poSRS, iZ < 0 ? wkbPoint : wkbPoint25D,
     370             :                         apszOptions);
     371           8 :                     poSRS->Release();
     372             :                 }
     373             :                 else
     374             :                 {
     375           8 :                     poCurLayer = m_poTmpDS->CreateLayer(osTablename, nullptr,
     376             :                                                         wkbNone, apszOptions);
     377             :                 }
     378          32 :                 if (poCurLayer == nullptr)
     379             :                 {
     380           0 :                     CSLDestroy(papszAtr);
     381           0 :                     CSLDestroy(papszFrm);
     382           0 :                     break;
     383             :                 }
     384             : 
     385          32 :                 if (!osAtr.empty() && CSLCount(papszAtr) == CSLCount(papszFrm))
     386             :                 {
     387          32 :                     OGRVDVParseAtrFrm(poCurLayer, nullptr, papszAtr, papszFrm);
     388             :                 }
     389          32 :                 CSLDestroy(papszAtr);
     390          32 :                 CSLDestroy(papszFrm);
     391             :             }
     392             : 
     393          80 :             OGRErr eErr = OGRERR_NONE;
     394             :             char **papszTokens =
     395          80 :                 CSLTokenizeStringComplex(pszLine + 4, ";", TRUE, TRUE);
     396          80 :             OGRFeatureDefn *poFDefn = poCurLayer->GetLayerDefn();
     397          80 :             OGRFeature *poFeature = new OGRFeature(poFDefn);
     398         405 :             for (int i = 0;
     399         405 :                  i < poFDefn->GetFieldCount() && papszTokens[i] != nullptr; i++)
     400             :             {
     401         325 :                 if (papszTokens[i][0])
     402             :                 {
     403         650 :                     if (bRecodeFromLatin1 &&
     404         325 :                         poFDefn->GetFieldDefn(i)->GetType() == OFTString)
     405             :                     {
     406         144 :                         char *pszRecoded = CPLRecode(
     407          72 :                             papszTokens[i], CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
     408          72 :                         poFeature->SetField(i, pszRecoded);
     409          72 :                         CPLFree(pszRecoded);
     410             :                     }
     411             :                     else
     412             :                     {
     413         253 :                         poFeature->SetField(i, papszTokens[i]);
     414             :                     }
     415             :                 }
     416             :             }
     417             : 
     418          80 :             if (eLayerType == LAYER_NODE && iX >= 0 && iY >= 0 && iNodeID >= 0)
     419             :             {
     420          16 :                 double dfX = poFeature->GetFieldAsDouble(iX);
     421          16 :                 double dfY = poFeature->GetFieldAsDouble(iY);
     422             :                 OGRGeometry *poGeom;
     423          16 :                 if (iZ >= 0)
     424             :                 {
     425           2 :                     double dfZ = poFeature->GetFieldAsDouble(iZ);
     426           2 :                     oMapNode[poFeature->GetFieldAsInteger64(iNodeID)] =
     427           2 :                         Point(dfX, dfY, dfZ);
     428           2 :                     poGeom = new OGRPoint(dfX, dfY, dfZ);
     429             :                 }
     430             :                 else
     431             :                 {
     432          14 :                     oMapNode[poFeature->GetFieldAsInteger64(iNodeID)] =
     433          14 :                         Point(dfX, dfY);
     434          14 :                     poGeom = new OGRPoint(dfX, dfY);
     435             :                 }
     436          16 :                 poGeom->assignSpatialReference(
     437          16 :                     poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
     438          16 :                 poFeature->SetGeometryDirectly(poGeom);
     439             :             }
     440          64 :             else if (eLayerType == LAYER_LINK && iFromNode >= 0 && iToNode >= 0)
     441             :             {
     442          32 :                 GIntBig nFromNode = poFeature->GetFieldAsInteger64(iFromNode);
     443          32 :                 GIntBig nToNode = poFeature->GetFieldAsInteger64(iToNode);
     444             :                 std::map<GIntBig, Point>::iterator oIterFrom =
     445          32 :                     oMapNode.find(nFromNode);
     446             :                 std::map<GIntBig, Point>::iterator oIterTo =
     447          32 :                     oMapNode.find(nToNode);
     448          32 :                 if (oIterFrom != oMapNode.end() && oIterTo != oMapNode.end())
     449             :                 {
     450          16 :                     OGRLineString *poLS = new OGRLineString();
     451          16 :                     if (iZ >= 0)
     452             :                     {
     453           2 :                         poLS->addPoint(oIterFrom->second.x, oIterFrom->second.y,
     454           2 :                                        oIterFrom->second.z);
     455           2 :                         poLS->addPoint(oIterTo->second.x, oIterTo->second.y,
     456           2 :                                        oIterTo->second.z);
     457             :                     }
     458             :                     else
     459             :                     {
     460          14 :                         poLS->addPoint(oIterFrom->second.x,
     461          14 :                                        oIterFrom->second.y);
     462          14 :                         poLS->addPoint(oIterTo->second.x, oIterTo->second.y);
     463             :                     }
     464          16 :                     poLS->assignSpatialReference(
     465          16 :                         poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
     466          16 :                     poFeature->SetGeometryDirectly(poLS);
     467          32 :                 }
     468             :             }
     469          32 :             else if (eLayerType == LAYER_LINKCOORDINATE && iX >= 0 && iY >= 0 &&
     470             :                      iLinkID >= 0)
     471             :             {
     472          24 :                 double dfX = poFeature->GetFieldAsDouble(iX);
     473          24 :                 double dfY = poFeature->GetFieldAsDouble(iY);
     474          24 :                 double dfZ = 0.0;
     475             :                 OGRGeometry *poGeom;
     476          24 :                 if (iZ >= 0)
     477             :                 {
     478           3 :                     dfZ = poFeature->GetFieldAsDouble(iZ);
     479           3 :                     poGeom = new OGRPoint(dfX, dfY, dfZ);
     480             :                 }
     481             :                 else
     482             :                 {
     483          21 :                     poGeom = new OGRPoint(dfX, dfY);
     484             :                 }
     485          24 :                 poGeom->assignSpatialReference(
     486          24 :                     poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
     487          24 :                 poFeature->SetGeometryDirectly(poGeom);
     488             : 
     489          24 :                 GIntBig nCurLinkID = poFeature->GetFieldAsInteger64(iLinkID);
     490             :                 std::map<GIntBig, OGRLineString *>::iterator
     491             :                     oMapLinkCoordinateIter =
     492          24 :                         oMapLinkCoordinate.find(nCurLinkID);
     493          24 :                 if (oMapLinkCoordinateIter == oMapLinkCoordinate.end())
     494             :                 {
     495          16 :                     OGRLineString *poLS = new OGRLineString();
     496          16 :                     if (iZ >= 0)
     497           2 :                         poLS->addPoint(dfX, dfY, dfZ);
     498             :                     else
     499          14 :                         poLS->addPoint(dfX, dfY);
     500          16 :                     oMapLinkCoordinate[nCurLinkID] = poLS;
     501             :                 }
     502             :                 else
     503             :                 {
     504           8 :                     if (iZ >= 0)
     505             :                     {
     506           1 :                         oMapLinkCoordinateIter->second->addPoint(dfX, dfY, dfZ);
     507             :                     }
     508             :                     else
     509             :                     {
     510           7 :                         oMapLinkCoordinateIter->second->addPoint(dfX, dfY);
     511             :                     }
     512             :                 }
     513             :             }
     514          80 :             eErr = poCurLayer->CreateFeature(poFeature);
     515          80 :             delete poFeature;
     516             : 
     517          80 :             CSLDestroy(papszTokens);
     518             : 
     519          80 :             if (eErr == OGRERR_FAILURE)
     520           0 :                 break;
     521             :         }
     522         304 :     }
     523             : 
     524           8 :     oMapNode.clear();
     525             : 
     526             :     // Patch Link geometries with the intermediate points of LinkCoordinate
     527           8 :     OGRLayer *poLinkLyr = m_poTmpDS->GetLayerByName("Link");
     528           8 :     if (poLinkLyr && poLinkLyr->GetLayerDefn()->GetGeomFieldCount())
     529             :     {
     530           8 :         iLinkID = poLinkLyr->GetLayerDefn()->GetFieldIndex("LINK_ID");
     531           8 :         if (iLinkID >= 0)
     532             :         {
     533           8 :             poLinkLyr->ResetReading();
     534             :             const OGRSpatialReference *poSRS =
     535           8 :                 poLinkLyr->GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef();
     536          40 :             for (auto &&poFeat : poLinkLyr)
     537             :             {
     538          32 :                 GIntBig nLinkID = poFeat->GetFieldAsInteger64(iLinkID);
     539             :                 std::map<GIntBig, OGRLineString *>::iterator
     540          32 :                     oMapLinkCoordinateIter = oMapLinkCoordinate.find(nLinkID);
     541          32 :                 OGRGeometry *poGeom = poFeat->GetGeometryRef();
     542          48 :                 if (poGeom &&
     543          48 :                     oMapLinkCoordinateIter != oMapLinkCoordinate.end())
     544             :                 {
     545           8 :                     OGRLineString *poLS = poGeom->toLineString();
     546           8 :                     if (poLS)
     547             :                     {
     548             :                         OGRLineString *poLSIntermediate =
     549           8 :                             oMapLinkCoordinateIter->second;
     550           8 :                         OGRLineString *poLSNew = new OGRLineString();
     551           8 :                         if (poLS->getGeometryType() == wkbLineString25D)
     552             :                         {
     553           1 :                             poLSNew->addPoint(poLS->getX(0), poLS->getY(0),
     554             :                                               poLS->getZ(0));
     555           3 :                             for (int i = 0;
     556           3 :                                  i < poLSIntermediate->getNumPoints(); i++)
     557             :                             {
     558           2 :                                 poLSNew->addPoint(poLSIntermediate->getX(i),
     559             :                                                   poLSIntermediate->getY(i),
     560             :                                                   poLSIntermediate->getZ(i));
     561             :                             }
     562           1 :                             poLSNew->addPoint(poLS->getX(1), poLS->getY(1),
     563             :                                               poLS->getZ(1));
     564             :                         }
     565             :                         else
     566             :                         {
     567           7 :                             poLSNew->addPoint(poLS->getX(0), poLS->getY(0));
     568          21 :                             for (int i = 0;
     569          21 :                                  i < poLSIntermediate->getNumPoints(); i++)
     570             :                             {
     571          14 :                                 poLSNew->addPoint(poLSIntermediate->getX(i),
     572             :                                                   poLSIntermediate->getY(i));
     573             :                             }
     574           7 :                             poLSNew->addPoint(poLS->getX(1), poLS->getY(1));
     575             :                         }
     576           8 :                         poLSNew->assignSpatialReference(poSRS);
     577           8 :                         poFeat->SetGeometryDirectly(poLSNew);
     578           8 :                         CPL_IGNORE_RET_VAL(poLinkLyr->SetFeature(poFeat.get()));
     579             :                     }
     580             :                 }
     581             :             }
     582           8 :             poLinkLyr->ResetReading();
     583             :         }
     584             :     }
     585             : 
     586           8 :     m_poTmpDS->CommitTransaction();
     587             : 
     588           8 :     if (bIsMEMLayer)
     589           7 :         m_poTmpDS->ExecuteSQL("PRAGMA read_only=1", nullptr, nullptr);
     590             : 
     591             :     std::map<GIntBig, OGRLineString *>::iterator oMapLinkCoordinateIter =
     592           8 :         oMapLinkCoordinate.begin();
     593          24 :     for (; oMapLinkCoordinateIter != oMapLinkCoordinate.end();
     594          16 :          ++oMapLinkCoordinateIter)
     595          16 :         delete oMapLinkCoordinateIter->second;
     596             : }
     597             : 
     598             : /************************************************************************/
     599             : /*                           GetLayerCount()                            */
     600             : /************************************************************************/
     601             : 
     602         450 : int OGRIDFDataSource::GetLayerCount()
     603             : {
     604         450 :     if (!m_bHasParsed)
     605           8 :         Parse();
     606         450 :     if (m_poTmpDS == nullptr)
     607           0 :         return 0;
     608         450 :     return m_poTmpDS->GetLayerCount();
     609             : }
     610             : 
     611             : /************************************************************************/
     612             : /*                              GetLayer()                              */
     613             : /************************************************************************/
     614             : 
     615         232 : OGRLayer *OGRIDFDataSource::GetLayer(int iLayer)
     616             : {
     617         232 :     if (iLayer < 0 || iLayer >= GetLayerCount())
     618           2 :         return nullptr;
     619         230 :     if (m_poTmpDS == nullptr)
     620           0 :         return nullptr;
     621         230 :     return m_poTmpDS->GetLayer(iLayer);
     622             : }
     623             : 
     624             : /************************************************************************/
     625             : /*                              TestCapability()                        */
     626             : /************************************************************************/
     627             : 
     628          27 : int OGRIDFDataSource::TestCapability(const char *pszCap)
     629             : {
     630          27 :     if (EQUAL(pszCap, ODsCMeasuredGeometries))
     631           8 :         return true;
     632          19 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     633           8 :         return true;
     634          11 :     else if (EQUAL(pszCap, ODsCZGeometries))
     635           8 :         return true;
     636             : 
     637           3 :     return false;
     638             : }
     639             : 
     640             : /************************************************************************/
     641             : /*                           OGRVDVDataSource()                         */
     642             : /************************************************************************/
     643             : 
     644         121 : OGRVDVDataSource::OGRVDVDataSource(const char *pszFilename, VSILFILE *fpL,
     645         121 :                                    bool bUpdate, bool bSingleFile, bool bNew)
     646             :     : m_osFilename(pszFilename), m_fpL(fpL), m_bUpdate(bUpdate),
     647             :       m_bSingleFile(bSingleFile), m_bNew(bNew),
     648         121 :       m_bLayersDetected(bNew || fpL == nullptr), m_nLayerCount(0),
     649             :       m_papoLayers(nullptr), m_poCurrentWriterLayer(nullptr),
     650         242 :       m_bMustWriteEof(false), m_bVDV452Loaded(false)
     651             : {
     652         121 : }
     653             : 
     654             : /************************************************************************/
     655             : /*                          ~OGRVDVDataSource()                         */
     656             : /************************************************************************/
     657             : 
     658         242 : OGRVDVDataSource::~OGRVDVDataSource()
     659             : {
     660         121 :     if (m_poCurrentWriterLayer)
     661             :     {
     662          27 :         m_poCurrentWriterLayer->StopAsCurrentLayer();
     663          27 :         m_poCurrentWriterLayer = nullptr;
     664             :     }
     665             : 
     666         309 :     for (int i = 0; i < m_nLayerCount; i++)
     667         188 :         delete m_papoLayers[i];
     668         121 :     CPLFree(m_papoLayers);
     669             : 
     670             :     // Close after destroying layers since they might use it (single file write)
     671         121 :     if (m_fpL)
     672             :     {
     673         109 :         if (m_bMustWriteEof)
     674             :         {
     675          48 :             VSIFPrintfL(m_fpL, "eof; %d\n", m_nLayerCount);
     676             :         }
     677         109 :         VSIFCloseL(m_fpL);
     678             :     }
     679         242 : }
     680             : 
     681             : /************************************************************************/
     682             : /*                           GetLayerCount()                            */
     683             : /************************************************************************/
     684             : 
     685        1079 : int OGRVDVDataSource::GetLayerCount()
     686             : {
     687        1079 :     if (!m_bLayersDetected)
     688          31 :         DetectLayers();
     689        1079 :     return m_nLayerCount;
     690             : }
     691             : 
     692             : /************************************************************************/
     693             : /*                              GetLayer()                              */
     694             : /************************************************************************/
     695             : 
     696         506 : OGRLayer *OGRVDVDataSource::GetLayer(int iLayer)
     697             : {
     698         506 :     if (iLayer < 0 || iLayer >= GetLayerCount())
     699           4 :         return nullptr;
     700         502 :     return m_papoLayers[iLayer];
     701             : }
     702             : 
     703             : /************************************************************************/
     704             : /*                         DetectLayers()                               */
     705             : /************************************************************************/
     706             : 
     707          31 : void OGRVDVDataSource::DetectLayers()
     708             : {
     709          31 :     m_bLayersDetected = true;
     710             : 
     711             :     char szBuffer[1 + 1024 + 1];
     712          31 :     char chNextExpected = 't';
     713          31 :     char chNextExpected2 = 'r';
     714          31 :     char chNextExpected3 = 'e';
     715          31 :     bool bInTableName = false;
     716          62 :     CPLString osTableName;
     717          31 :     GIntBig nFeatureCount = 0;
     718          31 :     vsi_l_offset nStartOffset = 0;
     719          31 :     OGRVDVLayer *poLayer = nullptr;
     720          31 :     bool bFirstBuffer = true;
     721          31 :     bool bRecodeFromLatin1 = false;
     722             : 
     723          31 :     VSIFSeekL(m_fpL, 0, SEEK_SET);
     724             : 
     725             :     while (true)
     726             :     {
     727          31 :         size_t nRead = VSIFReadL(szBuffer, 1, 1024, m_fpL);
     728          31 :         szBuffer[nRead] = '\0';
     729          31 :         if (bFirstBuffer)
     730             :         {
     731          31 :             const char *pszChs = strstr(szBuffer, "\nchs;");
     732          31 :             if (pszChs)
     733             :             {
     734          28 :                 pszChs += 5;
     735          28 :                 CPLString osChs;
     736         364 :                 for (; *pszChs != '\0' && *pszChs != '\r' && *pszChs != '\n';
     737             :                      ++pszChs)
     738             :                 {
     739         336 :                     if (*pszChs != ' ' && *pszChs != '"')
     740         252 :                         osChs += *pszChs;
     741             :                 }
     742          28 :                 bRecodeFromLatin1 =
     743          28 :                     EQUAL(osChs, "ISO8859-1") || EQUAL(osChs, "ISO_LATIN_1");
     744             :             }
     745          31 :             bFirstBuffer = false;
     746             :         }
     747       15561 :         for (size_t i = 0; i < nRead; i++)
     748             :         {
     749       15530 :             if (bInTableName)
     750             :             {
     751         640 :                 if (szBuffer[i] == '\r' || szBuffer[i] == '\n')
     752             :                 {
     753          70 :                     bInTableName = false;
     754          70 :                     poLayer = new OGRVDVLayer(this, osTableName, m_fpL, false,
     755          70 :                                               bRecodeFromLatin1, nStartOffset);
     756          70 :                     m_papoLayers = static_cast<OGRLayer **>(
     757         140 :                         CPLRealloc(m_papoLayers,
     758          70 :                                    sizeof(OGRLayer *) * (m_nLayerCount + 1)));
     759          70 :                     m_papoLayers[m_nLayerCount] = poLayer;
     760          70 :                     m_nLayerCount++;
     761             :                 }
     762         570 :                 else if (szBuffer[i] != ' ')
     763             :                 {
     764         500 :                     osTableName += szBuffer[i];
     765         500 :                     continue;
     766             :                 }
     767             :             }
     768             : 
     769             :             // Reset state on end of line characters
     770       15030 :             if (szBuffer[i] == '\n' || szBuffer[i] == '\r')
     771             :             {
     772         644 :                 chNextExpected = szBuffer[i];
     773         644 :                 chNextExpected2 = szBuffer[i];
     774         644 :                 chNextExpected3 = szBuffer[i];
     775             :             }
     776             : 
     777             :             // Detect tbl;
     778       15030 :             if (szBuffer[i] == chNextExpected)
     779             :             {
     780         924 :                 if (chNextExpected == '\n' || chNextExpected == '\r')
     781         644 :                     chNextExpected = 't';
     782         280 :                 else if (chNextExpected == 't')
     783          70 :                     chNextExpected = 'b';
     784         210 :                 else if (chNextExpected == 'b')
     785          70 :                     chNextExpected = 'l';
     786         140 :                 else if (chNextExpected == 'l')
     787          70 :                     chNextExpected = ';';
     788          70 :                 else if (chNextExpected == ';')
     789             :                 {
     790          70 :                     if (poLayer != nullptr)
     791           1 :                         poLayer->SetFeatureCount(nFeatureCount);
     792          70 :                     poLayer = nullptr;
     793          70 :                     nFeatureCount = 0;
     794          70 :                     nStartOffset = VSIFTellL(m_fpL) + i + 1 - nRead - 4;
     795          70 :                     bInTableName = true;
     796          70 :                     osTableName.resize(0);
     797          70 :                     chNextExpected = 0;
     798             :                 }
     799             :             }
     800             :             else
     801       14106 :                 chNextExpected = 0;
     802             : 
     803             :             // Detect rec;
     804       15030 :             if (szBuffer[i] == chNextExpected2)
     805             :             {
     806        1220 :                 if (chNextExpected2 == '\n' || chNextExpected2 == '\r')
     807         644 :                     chNextExpected2 = 'r';
     808         576 :                 else if (chNextExpected2 == 'r')
     809         144 :                     chNextExpected2 = 'e';
     810         432 :                 else if (chNextExpected2 == 'e')
     811         144 :                     chNextExpected2 = 'c';
     812         288 :                 else if (chNextExpected2 == 'c')
     813         144 :                     chNextExpected2 = ';';
     814         144 :                 else if (chNextExpected2 == ';')
     815             :                 {
     816         144 :                     nFeatureCount++;
     817         144 :                     chNextExpected2 = 0;
     818             :                 }
     819             :             }
     820             :             else
     821       13810 :                 chNextExpected2 = 0;
     822             : 
     823             :             // Detect end;
     824       15030 :             if (szBuffer[i] == chNextExpected3)
     825             :             {
     826         939 :                 if (chNextExpected3 == '\n' || chNextExpected3 == '\r')
     827         644 :                     chNextExpected3 = 'e';
     828         295 :                 else if (chNextExpected3 == 'e')
     829          94 :                     chNextExpected3 = 'n';
     830         201 :                 else if (chNextExpected3 == 'n')
     831          67 :                     chNextExpected3 = 'd';
     832         134 :                 else if (chNextExpected3 == 'd')
     833          67 :                     chNextExpected3 = ';';
     834          67 :                 else if (chNextExpected3 == ';')
     835             :                 {
     836          67 :                     if (poLayer != nullptr)
     837          67 :                         poLayer->SetFeatureCount(nFeatureCount);
     838          67 :                     poLayer = nullptr;
     839          67 :                     chNextExpected3 = 0;
     840             :                 }
     841             :             }
     842             :             else
     843       14091 :                 chNextExpected3 = 0;
     844             :         }
     845          31 :         if (nRead < 1024)
     846          31 :             break;
     847           0 :     }
     848          31 :     if (poLayer != nullptr)
     849           2 :         poLayer->SetFeatureCount(nFeatureCount);
     850          31 : }
     851             : 
     852             : /************************************************************************/
     853             : /*                           OGRVDVLayer()                              */
     854             : /************************************************************************/
     855             : 
     856         103 : OGRVDVLayer::OGRVDVLayer(GDALDataset *poDS, const CPLString &osTableName,
     857             :                          VSILFILE *fpL, bool bOwnFP, bool bRecodeFromLatin1,
     858         103 :                          vsi_l_offset nStartOffset)
     859             :     : m_poDS(poDS), m_fpL(fpL), m_bOwnFP(bOwnFP),
     860             :       m_bRecodeFromLatin1(bRecodeFromLatin1), m_nStartOffset(nStartOffset),
     861             :       m_nCurOffset(0), m_nTotalFeatureCount(0), m_nFID(0), m_bEOF(false),
     862         103 :       m_iLongitudeVDV452(-1), m_iLatitudeVDV452(-1)
     863             : {
     864         103 :     m_poFeatureDefn = new OGRFeatureDefn(osTableName);
     865         103 :     m_poFeatureDefn->SetGeomType(wkbNone);
     866         103 :     m_poFeatureDefn->Reference();
     867         103 :     SetDescription(osTableName);
     868         103 :     vsi_l_offset nCurOffset = VSIFTellL(fpL);
     869         103 :     VSIFSeekL(m_fpL, m_nStartOffset, SEEK_SET);
     870         206 :     CPLString osAtr, osFrm;
     871             : 
     872             :     /* skip until first tbl; */
     873         103 :     bool bFoundTbl = false;
     874         636 :     for (int i = 0; i < 20; i++)
     875             :     {
     876         636 :         const char *pszLine = CPLReadLineL(m_fpL);
     877         636 :         if (pszLine == nullptr)
     878           1 :             break;
     879         635 :         if (STARTS_WITH(pszLine, "chs;"))
     880             :         {
     881          32 :             CPLString osChs(pszLine + 4);
     882          32 :             osChs.Trim();
     883          32 :             if (osChs.size() >= 2 && osChs[0] == '"' && osChs.back() == '"')
     884          32 :                 osChs = osChs.substr(1, osChs.size() - 2);
     885          32 :             m_bRecodeFromLatin1 =
     886          32 :                 EQUAL(osChs, "ISO8859-1") || EQUAL(osChs, "ISO_LATIN_1");
     887             :         }
     888         603 :         else if (STARTS_WITH(pszLine, "tbl;"))
     889             :         {
     890         103 :             if (bFoundTbl)
     891           0 :                 break; /* shouldn't happen in correctly formed files */
     892         103 :             bFoundTbl = true;
     893         103 :             m_nStartOffset = VSIFTellL(fpL);
     894             :         }
     895         500 :         else if (STARTS_WITH(pszLine, "atr;"))
     896             :         {
     897         103 :             osAtr = pszLine + 4;
     898         103 :             osAtr.Trim();
     899             :         }
     900         397 :         else if (STARTS_WITH(pszLine, "frm;"))
     901             :         {
     902         103 :             osFrm = pszLine + 4;
     903         103 :             osFrm.Trim();
     904             :         }
     905         294 :         else if (STARTS_WITH(pszLine, "rec;") || STARTS_WITH(pszLine, "end;"))
     906             :             break;
     907             :     }
     908         103 :     if (!bFoundTbl)
     909           0 :         CPLDebug("VDV", "Didn't find tbl; line");
     910             : 
     911         103 :     VSIFSeekL(m_fpL, nCurOffset, SEEK_SET);
     912         103 :     if (!osAtr.empty() && !osFrm.empty())
     913             :     {
     914          74 :         char **papszAtr = CSLTokenizeString2(
     915             :             osAtr, ";",
     916             :             CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
     917          74 :         char **papszFrm = CSLTokenizeString2(
     918             :             osFrm, ";",
     919             :             CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
     920          74 :         if (CSLCount(papszAtr) == CSLCount(papszFrm))
     921             :         {
     922          74 :             OGRVDVParseAtrFrm(nullptr, m_poFeatureDefn, papszAtr, papszFrm);
     923             :         }
     924          74 :         CSLDestroy(papszAtr);
     925          74 :         CSLDestroy(papszFrm);
     926             :     }
     927             : 
     928             :     // Identify longitude, latitude columns of VDV-452 STOP table
     929         103 :     if (EQUAL(osTableName, "STOP")) /* English */
     930             :     {
     931           2 :         m_iLongitudeVDV452 = m_poFeatureDefn->GetFieldIndex("POINT_LONGITUDE");
     932           2 :         m_iLatitudeVDV452 = m_poFeatureDefn->GetFieldIndex("POINT_LATITUDE");
     933             :     }
     934         101 :     else if (EQUAL(osTableName, "REC_ORT")) /* German */
     935             :     {
     936           2 :         m_iLongitudeVDV452 = m_poFeatureDefn->GetFieldIndex("ORT_POS_LAENGE");
     937           2 :         m_iLatitudeVDV452 = m_poFeatureDefn->GetFieldIndex("ORT_POS_BREITE");
     938             :     }
     939         103 :     if (m_iLongitudeVDV452 >= 0 && m_iLatitudeVDV452 >= 0)
     940             :     {
     941           4 :         m_poFeatureDefn->SetGeomType(wkbPoint);
     942             :         OGRSpatialReference *poSRS =
     943           4 :             new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     944           4 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     945           4 :         m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     946           4 :         poSRS->Release();
     947             :     }
     948             :     else
     949          99 :         m_iLongitudeVDV452 = m_iLatitudeVDV452 = -1;
     950         103 : }
     951             : 
     952             : /************************************************************************/
     953             : /*                          ~OGRVDVLayer()                              */
     954             : /************************************************************************/
     955             : 
     956         206 : OGRVDVLayer::~OGRVDVLayer()
     957             : {
     958         103 :     m_poFeatureDefn->Release();
     959         103 :     if (m_bOwnFP)
     960          33 :         VSIFCloseL(m_fpL);
     961         206 : }
     962             : 
     963             : /************************************************************************/
     964             : /*                          ResetReading()                              */
     965             : /************************************************************************/
     966             : 
     967         502 : void OGRVDVLayer::ResetReading()
     968             : {
     969         502 :     VSIFSeekL(m_fpL, m_nStartOffset, SEEK_SET);
     970         502 :     m_nCurOffset = m_nStartOffset;
     971         502 :     m_nFID = 1;
     972         502 :     m_bEOF = false;
     973         502 : }
     974             : 
     975             : /************************************************************************/
     976             : /*                         OGRVDVUnescapeString()                       */
     977             : /************************************************************************/
     978             : 
     979         364 : static CPLString OGRVDVUnescapeString(const char *pszValue)
     980             : {
     981         364 :     CPLString osRet;
     982         868 :     for (; *pszValue != '\0'; ++pszValue)
     983             :     {
     984         504 :         if (*pszValue == '"' && pszValue[1] == '"')
     985             :         {
     986          68 :             osRet += '"';
     987          68 :             ++pszValue;
     988             :         }
     989             :         else
     990             :         {
     991         436 :             osRet += *pszValue;
     992             :         }
     993             :     }
     994         364 :     return osRet;
     995             : }
     996             : 
     997             : /************************************************************************/
     998             : /*                          GetNextFeature()                            */
     999             : /************************************************************************/
    1000             : 
    1001         548 : OGRFeature *OGRVDVLayer::GetNextFeature()
    1002             : {
    1003         548 :     if (m_nFID == 0)
    1004          12 :         ResetReading();
    1005         548 :     VSIFSeekL(m_fpL, m_nCurOffset, SEEK_SET);
    1006         548 :     OGRFeature *poFeature = nullptr;
    1007        1144 :     while (!m_bEOF)
    1008             :     {
    1009        1136 :         const char *pszLine = CPLReadLineL(m_fpL);
    1010        1136 :         if (pszLine == nullptr)
    1011           0 :             break;
    1012        1136 :         if (strncmp(pszLine, "end;", 4) == 0 ||
    1013         941 :             strncmp(pszLine, "tbl;", 4) == 0)
    1014             :         {
    1015         196 :             m_bEOF = true;
    1016         196 :             break;
    1017             :         }
    1018         940 :         if (strncmp(pszLine, "rec;", 4) != 0)
    1019         524 :             continue;
    1020             : 
    1021         416 :         char **papszTokens = CSLTokenizeString2(
    1022             :             pszLine + 4, ";",
    1023             :             CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
    1024         416 :         poFeature = new OGRFeature(m_poFeatureDefn);
    1025         416 :         poFeature->SetFID(m_nFID++);
    1026        1476 :         for (int i = 0;
    1027        1476 :              i < m_poFeatureDefn->GetFieldCount() && papszTokens[i] != nullptr;
    1028             :              i++)
    1029             :         {
    1030        1060 :             if (papszTokens[i][0] && !EQUAL(papszTokens[i], "NULL"))
    1031             :             {
    1032         508 :                 size_t nLen = strlen(papszTokens[i]);
    1033        1016 :                 CPLString osToken;
    1034         508 :                 if (nLen >= 2 && papszTokens[i][0] == '"' &&
    1035         364 :                     papszTokens[i][nLen - 1] == '"')
    1036             :                 {
    1037         364 :                     papszTokens[i][nLen - 1] = 0;
    1038         364 :                     osToken = OGRVDVUnescapeString(papszTokens[i] + 1);
    1039             :                 }
    1040             :                 else
    1041         144 :                     osToken = papszTokens[i];
    1042             :                 // Strip trailing spaces
    1043         508 :                 while (!osToken.empty() && osToken.back() == ' ')
    1044           0 :                     osToken.pop_back();
    1045             :                 OGRFieldType eFieldType =
    1046         508 :                     m_poFeatureDefn->GetFieldDefn(i)->GetType();
    1047         508 :                 if (m_bRecodeFromLatin1 && eFieldType == OFTString)
    1048             :                 {
    1049             :                     char *pszRecoded =
    1050         362 :                         CPLRecode(osToken, CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
    1051         362 :                     poFeature->SetField(i, pszRecoded);
    1052         362 :                     CPLFree(pszRecoded);
    1053             :                 }
    1054         146 :                 else if (eFieldType == OFTString || !EQUAL(osToken, "NULL"))
    1055             :                 {
    1056         146 :                     poFeature->SetField(i, osToken);
    1057             :                 }
    1058             :             }
    1059             :         }
    1060         416 :         CSLDestroy(papszTokens);
    1061             : 
    1062         416 :         if (m_iLongitudeVDV452 >= 0 && m_iLatitudeVDV452 >= 0)
    1063             :         {
    1064             :             int nLongDegMinMS =
    1065           4 :                 poFeature->GetFieldAsInteger(m_iLongitudeVDV452);
    1066           4 :             int nLongSign = 1;
    1067           4 :             if (nLongDegMinMS < 0)
    1068             :             {
    1069           4 :                 nLongSign = -1;
    1070           4 :                 nLongDegMinMS = -nLongDegMinMS;
    1071             :             }
    1072           4 :             const int nLongDeg = nLongDegMinMS / (100 * 100000);
    1073           4 :             const int nLongMin = (nLongDegMinMS / 100000) % 100;
    1074           4 :             const int nLongMS = nLongDegMinMS % 100000;
    1075           4 :             const double dfLong =
    1076           4 :                 (nLongDeg + nLongMin / 60.0 + nLongMS / (3600.0 * 1000.0)) *
    1077             :                 nLongSign;
    1078             : 
    1079           4 :             int nLatDegMinMS = poFeature->GetFieldAsInteger(m_iLatitudeVDV452);
    1080           4 :             int nLatSign = 1;
    1081           4 :             if (nLatDegMinMS < 0)
    1082             :             {
    1083           4 :                 nLatSign = -1;
    1084           4 :                 nLatDegMinMS = -nLatDegMinMS;
    1085             :             }
    1086           4 :             const int nLatDeg = nLatDegMinMS / (100 * 100000);
    1087           4 :             const int nLatMin = (nLatDegMinMS / 100000) % 100;
    1088           4 :             const int nLatMS = nLatDegMinMS % 100000;
    1089           4 :             const double dfLat =
    1090           4 :                 (nLatDeg + nLatMin / 60.0 + nLatMS / (3600.0 * 1000.0)) *
    1091             :                 nLatSign;
    1092             : 
    1093           4 :             if (dfLong != 0.0 || dfLat != 0.0)
    1094             :             {
    1095           4 :                 OGRPoint *poPoint = new OGRPoint(dfLong, dfLat);
    1096           4 :                 poPoint->assignSpatialReference(
    1097           4 :                     m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
    1098           4 :                 poFeature->SetGeometryDirectly(poPoint);
    1099             :             }
    1100             :         }
    1101             : 
    1102         832 :         if ((m_poFilterGeom == nullptr ||
    1103         832 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
    1104         416 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    1105             :         {
    1106         344 :             break;
    1107             :         }
    1108          72 :         delete poFeature;
    1109          72 :         poFeature = nullptr;
    1110             :     }
    1111         548 :     m_nCurOffset = VSIFTellL(m_fpL);
    1112         548 :     return poFeature;
    1113             : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                          TestCapability()                            */
    1117             : /************************************************************************/
    1118             : 
    1119         220 : int OGRVDVLayer::TestCapability(const char *pszCap)
    1120             : {
    1121         220 :     if (EQUAL(pszCap, OLCFastFeatureCount) && m_nTotalFeatureCount > 0 &&
    1122           0 :         m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1123             :     {
    1124           0 :         return TRUE;
    1125             :     }
    1126         220 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1127             :     {
    1128          60 :         return m_bRecodeFromLatin1;
    1129             :     }
    1130         160 :     else if (EQUAL(pszCap, OLCZGeometries))
    1131             :     {
    1132          24 :         return TRUE;
    1133             :     }
    1134         136 :     return FALSE;
    1135             : }
    1136             : 
    1137             : /************************************************************************/
    1138             : /*                          GetFeatureCount()                           */
    1139             : /************************************************************************/
    1140             : 
    1141          78 : GIntBig OGRVDVLayer::GetFeatureCount(int bForce)
    1142             : {
    1143          78 :     if (m_nTotalFeatureCount == 0 || m_poFilterGeom != nullptr ||
    1144          24 :         m_poAttrQuery != nullptr)
    1145             :     {
    1146          58 :         return OGRLayer::GetFeatureCount(bForce);
    1147             :     }
    1148          20 :     return m_nTotalFeatureCount;
    1149             : }
    1150             : 
    1151             : /************************************************************************/
    1152             : /*                              Identify()                              */
    1153             : /************************************************************************/
    1154             : 
    1155       46561 : static int OGRVDVDriverIdentify(GDALOpenInfo *poOpenInfo)
    1156             : 
    1157             : {
    1158       46561 :     if (poOpenInfo->bIsDirectory)
    1159        1038 :         return -1; /* perhaps... */
    1160             :     return (
    1161       47998 :         poOpenInfo->nHeaderBytes > 0 &&
    1162        2475 :         (strstr((const char *)poOpenInfo->pabyHeader, "\ntbl;") != nullptr ||
    1163        2324 :          strncmp((const char *)poOpenInfo->pabyHeader, "tbl;", 4) == 0) &&
    1164       48153 :         strstr((const char *)poOpenInfo->pabyHeader, "\natr;") != nullptr &&
    1165       45678 :         strstr((const char *)poOpenInfo->pabyHeader, "\nfrm;") != nullptr);
    1166             : }
    1167             : 
    1168             : /************************************************************************/
    1169             : /*                                Open()                                */
    1170             : /************************************************************************/
    1171             : 
    1172         585 : GDALDataset *OGRVDVDataSource::Open(GDALOpenInfo *poOpenInfo)
    1173             : 
    1174             : {
    1175         585 :     if (!OGRVDVDriverIdentify(poOpenInfo))
    1176             :     {
    1177           0 :         return nullptr;
    1178             :     }
    1179         585 :     if (poOpenInfo->bIsDirectory)
    1180             :     {
    1181         512 :         char **papszFiles = VSIReadDir(poOpenInfo->pszFilename);
    1182             : 
    1183             :         // Identify the extension with the most occurrences
    1184        1024 :         std::map<CPLString, int> oMapOtherExtensions;
    1185        1024 :         CPLString osMajorityExtension, osMajorityFile;
    1186         512 :         int nFiles = 0;
    1187       24007 :         for (char **papszIter = papszFiles; papszIter && *papszIter;
    1188             :              ++papszIter)
    1189             :         {
    1190       23495 :             if (EQUAL(*papszIter, ".") || EQUAL(*papszIter, ".."))
    1191         426 :                 continue;
    1192       23069 :             nFiles++;
    1193       46138 :             const std::string osExtension(CPLGetExtensionSafe(*papszIter));
    1194       23069 :             int nCount = ++oMapOtherExtensions[osExtension];
    1195       45539 :             if (osMajorityExtension == "" ||
    1196       22470 :                 nCount > oMapOtherExtensions[osMajorityExtension])
    1197             :             {
    1198        1791 :                 osMajorityExtension = osExtension;
    1199        1791 :                 osMajorityFile = *papszIter;
    1200             :             }
    1201             :         }
    1202             : 
    1203             :         // Check it is at least 50% of the files in the directory
    1204         989 :         if (osMajorityExtension == "" ||
    1205         477 :             2 * oMapOtherExtensions[osMajorityExtension] < nFiles)
    1206             :         {
    1207         438 :             CSLDestroy(papszFiles);
    1208         438 :             return nullptr;
    1209             :         }
    1210             : 
    1211             :         // And check that one of those files is a VDV one if it isn't .x10
    1212          74 :         if (osMajorityExtension != "x10")
    1213             :         {
    1214          73 :             GDALOpenInfo oOpenInfo(CPLFormFilenameSafe(poOpenInfo->pszFilename,
    1215             :                                                        osMajorityFile, nullptr)
    1216             :                                        .c_str(),
    1217          73 :                                    GA_ReadOnly);
    1218          73 :             if (OGRVDVDriverIdentify(&oOpenInfo) != TRUE)
    1219             :             {
    1220          64 :                 CSLDestroy(papszFiles);
    1221          64 :                 return nullptr;
    1222             :             }
    1223             :         }
    1224             : 
    1225             :         OGRVDVDataSource *poDS = new OGRVDVDataSource(
    1226          10 :             poOpenInfo->pszFilename, nullptr,        /* fp */
    1227          10 :             poOpenInfo->eAccess == GA_Update, false, /* single file */
    1228          10 :             false /* new */);
    1229             : 
    1230             :         // Instantiate the layers.
    1231          63 :         for (char **papszIter = papszFiles; papszIter && *papszIter;
    1232             :              ++papszIter)
    1233             :         {
    1234          53 :             if (!EQUAL(CPLGetExtensionSafe(*papszIter).c_str(),
    1235             :                        osMajorityExtension))
    1236          20 :                 continue;
    1237             :             VSILFILE *fp =
    1238          33 :                 VSIFOpenL(CPLFormFilenameSafe(poOpenInfo->pszFilename,
    1239             :                                               *papszIter, nullptr)
    1240             :                               .c_str(),
    1241             :                           "rb");
    1242          33 :             if (fp == nullptr)
    1243           0 :                 continue;
    1244          33 :             poDS->m_papoLayers = static_cast<OGRLayer **>(
    1245          66 :                 CPLRealloc(poDS->m_papoLayers,
    1246          33 :                            sizeof(OGRLayer *) * (poDS->m_nLayerCount + 1)));
    1247          66 :             poDS->m_papoLayers[poDS->m_nLayerCount] =
    1248          66 :                 new OGRVDVLayer(poDS, CPLGetBasenameSafe(*papszIter).c_str(),
    1249          33 :                                 fp, true, false, 0);
    1250          33 :             poDS->m_nLayerCount++;
    1251             :         }
    1252          10 :         CSLDestroy(papszFiles);
    1253             : 
    1254          10 :         if (poDS->m_nLayerCount == 0)
    1255             :         {
    1256           0 :             delete poDS;
    1257           0 :             poDS = nullptr;
    1258             :         }
    1259          10 :         return poDS;
    1260             :     }
    1261             : 
    1262          73 :     VSILFILE *fpL = poOpenInfo->fpL;
    1263          73 :     poOpenInfo->fpL = nullptr;
    1264          73 :     const char *pszHeader = (const char *)poOpenInfo->pabyHeader;
    1265          73 :     if (strstr(pszHeader, "tbl;Node\r\natr;NODE_ID;") != nullptr ||
    1266          65 :         strstr(pszHeader, "tbl;Node\natr;NODE_ID;") != nullptr ||
    1267          65 :         strstr(pszHeader, "tbl;Link\r\natr;LINK_ID;") != nullptr ||
    1268          65 :         strstr(pszHeader, "tbl;Link\natr;LINK_ID;") != nullptr ||
    1269          65 :         strstr(pszHeader, "tbl;LinkCoordinate\r\natr;LINK_ID;") != nullptr ||
    1270          65 :         strstr(pszHeader, "tbl;LinkCoordinate\natr;LINK_ID;") != nullptr)
    1271             :     {
    1272           8 :         return new OGRIDFDataSource(poOpenInfo->pszFilename, fpL);
    1273             :     }
    1274             :     else
    1275             :     {
    1276          65 :         return new OGRVDVDataSource(poOpenInfo->pszFilename, fpL,
    1277          65 :                                     poOpenInfo->eAccess == GA_Update,
    1278             :                                     true, /* single file */
    1279          65 :                                     false /* new */);
    1280             :     }
    1281             : }
    1282             : 
    1283             : /************************************************************************/
    1284             : /*                         OGRVDVWriterLayer                            */
    1285             : /************************************************************************/
    1286             : 
    1287          85 : OGRVDVWriterLayer::OGRVDVWriterLayer(OGRVDVDataSource *poDS,
    1288             :                                      const char *pszName, VSILFILE *fpL,
    1289             :                                      bool bOwnFP, OGRVDV452Table *poVDV452Table,
    1290             :                                      const CPLString &osVDV452Lang,
    1291          85 :                                      bool bProfileStrict)
    1292          85 :     : m_poDS(poDS), m_poFeatureDefn(new OGRFeatureDefn(pszName)),
    1293             :       m_bWritePossible(true), m_fpL(fpL), m_bOwnFP(bOwnFP), m_nFeatureCount(-1),
    1294             :       m_poVDV452Table(poVDV452Table), m_osVDV452Lang(osVDV452Lang),
    1295             :       m_bProfileStrict(bProfileStrict), m_iLongitudeVDV452(-1),
    1296         170 :       m_iLatitudeVDV452(-1)
    1297             : {
    1298          85 :     m_poFeatureDefn->SetGeomType(wkbNone);
    1299          85 :     m_poFeatureDefn->Reference();
    1300          85 :     SetDescription(pszName);
    1301          85 : }
    1302             : 
    1303             : /************************************************************************/
    1304             : /*                        ~OGRVDVWriterLayer                            */
    1305             : /************************************************************************/
    1306             : 
    1307         170 : OGRVDVWriterLayer::~OGRVDVWriterLayer()
    1308             : {
    1309          85 :     StopAsCurrentLayer();
    1310             : 
    1311          85 :     m_poFeatureDefn->Release();
    1312          85 :     if (m_bOwnFP)
    1313             :     {
    1314           8 :         VSIFPrintfL(m_fpL, "eof; %d\n", 1);
    1315           8 :         VSIFCloseL(m_fpL);
    1316             :     }
    1317         170 : }
    1318             : 
    1319             : /************************************************************************/
    1320             : /*                          ResetReading()                              */
    1321             : /************************************************************************/
    1322             : 
    1323          17 : void OGRVDVWriterLayer::ResetReading()
    1324             : {
    1325          17 : }
    1326             : 
    1327             : /************************************************************************/
    1328             : /*                          GetNextFeature()                            */
    1329             : /************************************************************************/
    1330             : 
    1331          17 : OGRFeature *OGRVDVWriterLayer::GetNextFeature()
    1332             : {
    1333          17 :     CPLError(CE_Failure, CPLE_NotSupported,
    1334             :              "GetNextFeature() not supported on write-only layer");
    1335          17 :     return nullptr;
    1336             : }
    1337             : 
    1338             : /************************************************************************/
    1339             : /*                         OGRVDVEscapeString()                         */
    1340             : /************************************************************************/
    1341             : 
    1342         648 : static CPLString OGRVDVEscapeString(const char *pszValue)
    1343             : {
    1344         648 :     CPLString osRet;
    1345        4672 :     for (; *pszValue != '\0'; ++pszValue)
    1346             :     {
    1347        4024 :         if (*pszValue == '"')
    1348           6 :             osRet += "\"\"";
    1349             :         else
    1350        4018 :             osRet += *pszValue;
    1351             :     }
    1352         648 :     return osRet;
    1353             : }
    1354             : 
    1355             : /************************************************************************/
    1356             : /*                          WriteSchemaIfNeeded()                       */
    1357             : /************************************************************************/
    1358             : 
    1359         215 : bool OGRVDVWriterLayer::WriteSchemaIfNeeded()
    1360             : {
    1361         215 :     if (m_nFeatureCount < 0)
    1362             :     {
    1363          85 :         m_nFeatureCount = 0;
    1364             : 
    1365             :         bool bOK =
    1366          85 :             VSIFPrintfL(m_fpL, "tbl; %s\n", m_poFeatureDefn->GetName()) > 0;
    1367          85 :         bOK &= VSIFPrintfL(m_fpL, "atr;") > 0;
    1368         346 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    1369             :         {
    1370         261 :             if (i > 0)
    1371         208 :                 bOK &= VSIFPrintfL(m_fpL, ";") > 0;
    1372         261 :             bOK &=
    1373         261 :                 VSIFPrintfL(m_fpL, " %s",
    1374         522 :                             m_poFeatureDefn->GetFieldDefn(i)->GetNameRef()) > 0;
    1375             :         }
    1376          85 :         bOK &= VSIFPrintfL(m_fpL, "\n") > 0;
    1377          85 :         bOK &= VSIFPrintfL(m_fpL, "frm;") > 0;
    1378         346 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    1379             :         {
    1380         261 :             if (i > 0)
    1381         208 :                 bOK &= VSIFPrintfL(m_fpL, ";") > 0;
    1382         261 :             bOK &= VSIFPrintfL(m_fpL, " ") > 0;
    1383         261 :             int nWidth = m_poFeatureDefn->GetFieldDefn(i)->GetWidth();
    1384             :             const OGRFieldType eType =
    1385         261 :                 m_poFeatureDefn->GetFieldDefn(i)->GetType();
    1386         261 :             switch (eType)
    1387             :             {
    1388         131 :                 case OFTInteger:
    1389             :                 case OFTInteger64:
    1390         131 :                     if (m_poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    1391             :                         OFSTBoolean)
    1392             :                     {
    1393           6 :                         bOK &= VSIFPrintfL(m_fpL, "boolean") > 0;
    1394             :                     }
    1395             :                     else
    1396             :                     {
    1397         125 :                         if (nWidth == 0)
    1398             :                         {
    1399          24 :                             if (eType == OFTInteger)
    1400          20 :                                 nWidth = 11;
    1401             :                             else
    1402           4 :                                 nWidth = 20;
    1403             :                         }
    1404         125 :                         nWidth--; /* VDV 451 is without sign */
    1405         125 :                         bOK &= VSIFPrintfL(m_fpL, "num[%d.0]", nWidth) > 0;
    1406             :                     }
    1407         131 :                     break;
    1408             : 
    1409         130 :                 default:
    1410         130 :                     if (nWidth == 0)
    1411             :                     {
    1412          92 :                         nWidth = 80;
    1413             :                     }
    1414         130 :                     bOK &= VSIFPrintfL(m_fpL, "char[%d]", nWidth) > 0;
    1415         130 :                     break;
    1416             :             }
    1417             :         }
    1418          85 :         bOK &= VSIFPrintfL(m_fpL, "\n") > 0;
    1419             : 
    1420          85 :         if (!bOK)
    1421           0 :             return false;
    1422             :     }
    1423             : 
    1424         215 :     return true;
    1425             : }
    1426             : 
    1427             : /************************************************************************/
    1428             : /*                         ICreateFeature()                             */
    1429             : /************************************************************************/
    1430             : 
    1431         131 : OGRErr OGRVDVWriterLayer::ICreateFeature(OGRFeature *poFeature)
    1432             : {
    1433         131 :     if (!m_bWritePossible)
    1434             :     {
    1435           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1436             :                  "Layer %s is no longer the active layer. "
    1437             :                  "Writing in it is no longer possible",
    1438           1 :                  m_poFeatureDefn->GetName());
    1439           1 :         return OGRERR_FAILURE;
    1440             :     }
    1441         130 :     m_poDS->SetCurrentWriterLayer(this);
    1442             : 
    1443         130 :     WriteSchemaIfNeeded();
    1444             : 
    1445         130 :     bool bOK = VSIFPrintfL(m_fpL, "rec; ") > 0;
    1446         638 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    1447             :     {
    1448         508 :         if (i > 0)
    1449         380 :             bOK &= VSIFPrintfL(m_fpL, "; ") > 0;
    1450         508 :         auto poGeom = poFeature->GetGeometryRef();
    1451         508 :         if (poFeature->IsFieldSetAndNotNull(i))
    1452             :         {
    1453             :             const OGRFieldType eType =
    1454         290 :                 m_poFeatureDefn->GetFieldDefn(i)->GetType();
    1455         290 :             if (eType == OFTInteger || eType == OFTInteger64)
    1456             :             {
    1457          60 :                 bOK &= VSIFPrintfL(m_fpL, CPL_FRMT_GIB,
    1458          60 :                                    poFeature->GetFieldAsInteger64(i)) > 0;
    1459             :             }
    1460             :             else
    1461             :             {
    1462         230 :                 char *pszRecoded = CPLRecode(poFeature->GetFieldAsString(i),
    1463             :                                              CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1464         230 :                 bOK &= VSIFPrintfL(m_fpL, "\"%s\"",
    1465         460 :                                    OGRVDVEscapeString(pszRecoded).c_str()) > 0;
    1466         230 :                 CPLFree(pszRecoded);
    1467             :             }
    1468             :         }
    1469         222 :         else if (i == m_iLongitudeVDV452 && poGeom != nullptr &&
    1470           4 :                  poGeom->getGeometryType() == wkbPoint)
    1471             :         {
    1472           4 :             OGRPoint *poPoint = poGeom->toPoint();
    1473           4 :             const double dfDeg = poPoint->getX();
    1474           4 :             const double dfAbsDeg = fabs(dfDeg);
    1475           4 :             const int nDeg = static_cast<int>(dfAbsDeg);
    1476           4 :             const int nMin = static_cast<int>((dfAbsDeg - nDeg) * 60);
    1477           4 :             const double dfSec = (dfAbsDeg - nDeg) * 3600 - nMin * 60;
    1478           4 :             const int nSec = static_cast<int>(dfSec);
    1479           4 :             int nMS = static_cast<int>((dfSec - nSec) * 1000 + 0.5);
    1480           4 :             if (nMS == 1000)
    1481           0 :                 nMS = 999;
    1482           4 :             if (dfDeg < 0)
    1483           4 :                 bOK &= VSIFPrintfL(m_fpL, "-") > 0;
    1484           4 :             bOK &= VSIFPrintfL(m_fpL, "%03d%02d%02d%03d", nDeg, nMin, nSec,
    1485           4 :                                nMS) > 0;
    1486             :         }
    1487         218 :         else if (i == m_iLatitudeVDV452 && poGeom != nullptr &&
    1488           4 :                  poGeom->getGeometryType() == wkbPoint)
    1489             :         {
    1490           4 :             OGRPoint *poPoint = poGeom->toPoint();
    1491           4 :             const double dfDeg = poPoint->getY();
    1492           4 :             const double dfAbsDeg = fabs(dfDeg);
    1493           4 :             const int nDeg = static_cast<int>(dfAbsDeg);
    1494           4 :             const int nMin = static_cast<int>((dfAbsDeg - nDeg) * 60);
    1495           4 :             const double dfSec = (dfAbsDeg - nDeg) * 3600 - nMin * 60;
    1496           4 :             const int nSec = static_cast<int>(dfSec);
    1497           4 :             int nMS = static_cast<int>((dfSec - nSec) * 1000 + 0.5);
    1498           4 :             if (nMS == 1000)
    1499           0 :                 nMS = 999;
    1500           4 :             if (dfDeg < 0)
    1501           4 :                 bOK &= VSIFPrintfL(m_fpL, "-") > 0;
    1502           4 :             bOK &= VSIFPrintfL(m_fpL, "%02d%02d%02d%03d", nDeg, nMin, nSec,
    1503           4 :                                nMS) > 0;
    1504             :         }
    1505             :         else
    1506             :         {
    1507         210 :             bOK &= VSIFPrintfL(m_fpL, "NULL") > 0;
    1508             :         }
    1509             :     }
    1510         130 :     bOK &= VSIFPrintfL(m_fpL, "\n") > 0;
    1511             : 
    1512         130 :     if (!bOK)
    1513           0 :         return OGRERR_FAILURE;
    1514             : 
    1515         130 :     m_nFeatureCount++;
    1516         130 :     return OGRERR_NONE;
    1517             : }
    1518             : 
    1519             : /************************************************************************/
    1520             : /*                         GetFeatureCount()                            */
    1521             : /************************************************************************/
    1522             : 
    1523           1 : GIntBig OGRVDVWriterLayer::GetFeatureCount(int)
    1524             : {
    1525           1 :     return m_nFeatureCount >= 0 ? m_nFeatureCount : 0;
    1526             : }
    1527             : 
    1528             : /************************************************************************/
    1529             : /*                          CreateField()                               */
    1530             : /************************************************************************/
    1531             : 
    1532         263 : OGRErr OGRVDVWriterLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    1533             :                                       int /* bApprox */)
    1534             : {
    1535         263 :     if (m_nFeatureCount >= 0)
    1536             :     {
    1537           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1538             :                  "Fields can no longer by added to layer %s",
    1539           1 :                  m_poFeatureDefn->GetName());
    1540           1 :         return OGRERR_FAILURE;
    1541             :     }
    1542             : 
    1543         262 :     if (m_poVDV452Table != nullptr)
    1544             :     {
    1545         122 :         bool bFound = false;
    1546        1125 :         for (size_t i = 0; i < m_poVDV452Table->aosFields.size(); i++)
    1547             :         {
    1548        1122 :             const char *pszFieldName = poFieldDefn->GetNameRef();
    1549        1122 :             if ((m_osVDV452Lang == "en" &&
    1550         646 :                  EQUAL(m_poVDV452Table->aosFields[i].osEnglishName,
    1551        2720 :                        pszFieldName)) ||
    1552        1054 :                 (m_osVDV452Lang == "de" &&
    1553         476 :                  EQUAL(m_poVDV452Table->aosFields[i].osGermanName,
    1554             :                        pszFieldName)))
    1555             :             {
    1556         119 :                 bFound = true;
    1557         119 :                 break;
    1558             :             }
    1559             :         }
    1560         122 :         if (!bFound)
    1561             :         {
    1562           3 :             CPLError(m_bProfileStrict ? CE_Failure : CE_Warning,
    1563             :                      CPLE_AppDefined,
    1564             :                      "Field %s is not an allowed field for table %s",
    1565           3 :                      poFieldDefn->GetNameRef(), m_poFeatureDefn->GetName());
    1566           3 :             if (m_bProfileStrict)
    1567           1 :                 return OGRERR_FAILURE;
    1568             :         }
    1569         173 :         if (EQUAL(m_poFeatureDefn->GetName(), "STOP") ||
    1570          52 :             EQUAL(m_poFeatureDefn->GetName(), "REC_ORT"))
    1571             :         {
    1572         238 :             if (EQUAL(poFieldDefn->GetNameRef(), "POINT_LONGITUDE") ||
    1573         117 :                 EQUAL(poFieldDefn->GetNameRef(), "ORT_POS_LAENGE"))
    1574             :             {
    1575           7 :                 m_iLongitudeVDV452 = m_poFeatureDefn->GetFieldCount();
    1576             :             }
    1577         224 :             else if (EQUAL(poFieldDefn->GetNameRef(), "POINT_LATITUDE") ||
    1578         110 :                      EQUAL(poFieldDefn->GetNameRef(), "ORT_POS_BREITE"))
    1579             :             {
    1580           7 :                 m_iLatitudeVDV452 = m_poFeatureDefn->GetFieldCount();
    1581             :             }
    1582             :         }
    1583             :     }
    1584             : 
    1585         261 :     m_poFeatureDefn->AddFieldDefn(poFieldDefn);
    1586         261 :     return OGRERR_NONE;
    1587             : }
    1588             : 
    1589             : /************************************************************************/
    1590             : /*                         TestCapability()                             */
    1591             : /************************************************************************/
    1592             : 
    1593         140 : int OGRVDVWriterLayer::TestCapability(const char *pszCap)
    1594             : {
    1595         140 :     if (EQUAL(pszCap, OLCSequentialWrite))
    1596          18 :         return m_bWritePossible;
    1597         122 :     if (EQUAL(pszCap, OLCCreateField))
    1598          18 :         return m_nFeatureCount < 0;
    1599         104 :     return FALSE;
    1600             : }
    1601             : 
    1602             : /************************************************************************/
    1603             : /*                         StopAsCurrentLayer()                         */
    1604             : /************************************************************************/
    1605             : 
    1606         133 : void OGRVDVWriterLayer::StopAsCurrentLayer()
    1607             : {
    1608         133 :     if (m_bWritePossible)
    1609             :     {
    1610          85 :         m_bWritePossible = false;
    1611          85 :         if (m_fpL != nullptr)
    1612             :         {
    1613          85 :             WriteSchemaIfNeeded();
    1614          85 :             VSIFPrintfL(m_fpL, "end; " CPL_FRMT_GIB "\n", m_nFeatureCount);
    1615             :         }
    1616             :     }
    1617         133 : }
    1618             : 
    1619             : /************************************************************************/
    1620             : /*                             GetDataset()                             */
    1621             : /************************************************************************/
    1622             : 
    1623          20 : GDALDataset *OGRVDVWriterLayer::GetDataset()
    1624             : {
    1625          20 :     return m_poDS;
    1626             : }
    1627             : 
    1628             : /************************************************************************/
    1629             : /*                         OGRVDVWriteHeader()                          */
    1630             : /************************************************************************/
    1631             : 
    1632          52 : static bool OGRVDVWriteHeader(VSILFILE *fpL, CSLConstList papszOptions)
    1633             : {
    1634          52 :     bool bRet = true;
    1635             :     const bool bStandardHeader =
    1636          52 :         CPLFetchBool(papszOptions, "STANDARD_HEADER", true);
    1637             : 
    1638             :     struct tm tm;
    1639          52 :     CPLUnixTimeToYMDHMS(time(nullptr), &tm);
    1640          52 :     const char *pszSrc = CSLFetchNameValueDef(
    1641             :         papszOptions, "HEADER_SRC", (bStandardHeader) ? "UNKNOWN" : nullptr);
    1642         104 :     const char *pszSrcDate = CSLFetchNameValueDef(
    1643             :         papszOptions, "HEADER_SRC_DATE",
    1644          52 :         (pszSrc) ? CPLSPrintf("%02d.%02d.%04d", tm.tm_mday, tm.tm_mon + 1,
    1645          52 :                               tm.tm_year + 1900)
    1646             :                  : nullptr);
    1647             :     const char *pszSrcTime =
    1648         104 :         CSLFetchNameValueDef(papszOptions, "HEADER_SRC_TIME",
    1649          52 :                              (pszSrc) ? CPLSPrintf("%02d.%02d.%02d", tm.tm_hour,
    1650             :                                                    tm.tm_min, tm.tm_sec)
    1651             :                                       : nullptr);
    1652             : 
    1653          52 :     if (pszSrc && pszSrcDate && pszSrcTime)
    1654             :     {
    1655          52 :         bRet &= VSIFPrintfL(fpL, "mod; DD.MM.YYYY; HH:MM:SS; free\n") > 0;
    1656         156 :         bRet &= VSIFPrintfL(fpL, "src; \"%s\"; \"%s\"; \"%s\"\n",
    1657          52 :                             OGRVDVEscapeString(pszSrc).c_str(),
    1658         104 :                             OGRVDVEscapeString(pszSrcDate).c_str(),
    1659         156 :                             OGRVDVEscapeString(pszSrcTime).c_str()) > 0;
    1660             :     }
    1661             : 
    1662          52 :     if (bStandardHeader)
    1663             :     {
    1664             :         const char *pszChs =
    1665          52 :             CSLFetchNameValueDef(papszOptions, "HEADER_CHS", "ISO8859-1");
    1666             :         const char *pszVer =
    1667          52 :             CSLFetchNameValueDef(papszOptions, "HEADER_VER", "1.4");
    1668             :         const char *pszIfv =
    1669          52 :             CSLFetchNameValueDef(papszOptions, "HEADER_IFV", "1.4");
    1670             :         const char *pszDve =
    1671          52 :             CSLFetchNameValueDef(papszOptions, "HEADER_DVE", "1.4");
    1672             :         const char *pszFft =
    1673          52 :             CSLFetchNameValueDef(papszOptions, "HEADER_FFT", "");
    1674             : 
    1675          52 :         bRet &= VSIFPrintfL(fpL, "chs; \"%s\"\n",
    1676         104 :                             OGRVDVEscapeString(pszChs).c_str()) > 0;
    1677          52 :         bRet &= VSIFPrintfL(fpL, "ver; \"%s\"\n",
    1678         104 :                             OGRVDVEscapeString(pszVer).c_str()) > 0;
    1679          52 :         bRet &= VSIFPrintfL(fpL, "ifv; \"%s\"\n",
    1680         104 :                             OGRVDVEscapeString(pszIfv).c_str()) > 0;
    1681          52 :         bRet &= VSIFPrintfL(fpL, "dve; \"%s\"\n",
    1682         104 :                             OGRVDVEscapeString(pszDve).c_str()) > 0;
    1683          52 :         bRet &= VSIFPrintfL(fpL, "fft; \"%s\"\n",
    1684         104 :                             OGRVDVEscapeString(pszFft).c_str()) > 0;
    1685             :     }
    1686             : 
    1687          76 :     for (CSLConstList papszIter = papszOptions;
    1688          76 :          papszIter != nullptr && *papszIter != nullptr; papszIter++)
    1689             :     {
    1690          24 :         if (STARTS_WITH_CI(*papszIter, "HEADER_") &&
    1691           6 :             !STARTS_WITH_CI(*papszIter, "HEADER_SRC") &&
    1692           2 :             (!bStandardHeader || (!EQUAL(*papszIter, "HEADER_CHS") &&
    1693           2 :                                   !EQUAL(*papszIter, "HEADER_VER") &&
    1694           2 :                                   !EQUAL(*papszIter, "HEADER_IFV") &&
    1695           2 :                                   !EQUAL(*papszIter, "HEADER_DVE") &&
    1696           2 :                                   !EQUAL(*papszIter, "HEADER_FFT"))))
    1697             :         {
    1698           2 :             char *pszKey = nullptr;
    1699           2 :             const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
    1700           2 :             if (pszKey && strlen(pszKey) > strlen("HEADER_") && pszValue)
    1701             :             {
    1702           2 :                 bRet &=
    1703           2 :                     VSIFPrintfL(fpL, "%s; \"%s\"\n", pszKey + strlen("HEADER_"),
    1704           4 :                                 OGRVDVEscapeString(pszValue).c_str()) > 0;
    1705             :             }
    1706           2 :             CPLFree(pszKey);
    1707             :         }
    1708             :     }
    1709             : 
    1710          52 :     return bRet;
    1711             : }
    1712             : 
    1713             : /************************************************************************/
    1714             : /*                      OGRVDVLoadVDV452Tables()                        */
    1715             : /************************************************************************/
    1716             : 
    1717           7 : static bool OGRVDVLoadVDV452Tables(OGRVDV452Tables &oTables)
    1718             : {
    1719           7 :     CPLXMLNode *psRoot = nullptr;
    1720             : #if defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
    1721             :     const char *pszXMLDescFilename = nullptr;
    1722             : #else
    1723           7 :     const char *pszXMLDescFilename = CPLFindFile("gdal", "vdv452.xml");
    1724             : #endif
    1725           7 :     if (pszXMLDescFilename == nullptr ||
    1726           7 :         EQUAL(pszXMLDescFilename, "vdv452.xml"))
    1727             :     {
    1728             : #ifdef EMBED_RESOURCE_FILES
    1729             :         static const bool bOnce [[maybe_unused]] = []()
    1730             :         {
    1731             :             CPLDebug("VDV", "Using embedded vdv452.xml");
    1732             :             return true;
    1733             :         }();
    1734             :         psRoot = CPLParseXMLString(VDVGet452XML());
    1735             : #else
    1736           0 :         CPLDebug("VDV", "Cannot find XML file : %s", "vdv452.xml");
    1737           0 :         return false;
    1738             : #endif
    1739             :     }
    1740             : 
    1741             : #ifdef EMBED_RESOURCE_FILES
    1742             :     if (!psRoot)
    1743             : #endif
    1744             :     {
    1745           7 :         psRoot = CPLParseXMLFile(pszXMLDescFilename);
    1746             :     }
    1747           7 :     if (psRoot == nullptr)
    1748             :     {
    1749           0 :         return false;
    1750             :     }
    1751           7 :     CPLXMLNode *psTables = CPLGetXMLNode(psRoot, "=Layers");
    1752           7 :     if (psTables != nullptr)
    1753             :     {
    1754         245 :         for (CPLXMLNode *psTable = psTables->psChild; psTable != nullptr;
    1755         238 :              psTable = psTable->psNext)
    1756             :         {
    1757         238 :             if (psTable->eType != CXT_Element ||
    1758         238 :                 strcmp(psTable->pszValue, "Layer") != 0)
    1759           0 :                 continue;
    1760         238 :             OGRVDV452Table *poTable = new OGRVDV452Table();
    1761         238 :             poTable->osEnglishName = CPLGetXMLValue(psTable, "name_en", "");
    1762         238 :             poTable->osGermanName = CPLGetXMLValue(psTable, "name_de", "");
    1763         238 :             oTables.aosTables.push_back(poTable);
    1764         238 :             oTables.oMapEnglish[poTable->osEnglishName] = poTable;
    1765         238 :             oTables.oMapGerman[poTable->osGermanName] = poTable;
    1766        2569 :             for (CPLXMLNode *psField = psTable->psChild; psField != nullptr;
    1767        2331 :                  psField = psField->psNext)
    1768             :             {
    1769        2331 :                 if (psField->eType != CXT_Element ||
    1770        1617 :                     strcmp(psField->pszValue, "Field") != 0)
    1771         714 :                     continue;
    1772        3234 :                 OGRVDV452Field oField;
    1773        1617 :                 oField.osEnglishName = CPLGetXMLValue(psField, "name_en", "");
    1774        1617 :                 oField.osGermanName = CPLGetXMLValue(psField, "name_de", "");
    1775        1617 :                 oField.osType = CPLGetXMLValue(psField, "type", "");
    1776        1617 :                 oField.nWidth = atoi(CPLGetXMLValue(psField, "width", "0"));
    1777        1617 :                 poTable->aosFields.push_back(oField);
    1778             :             }
    1779             :         }
    1780             :     }
    1781             : 
    1782           7 :     CPLDestroyXMLNode(psRoot);
    1783           7 :     return true;
    1784             : }
    1785             : 
    1786             : /************************************************************************/
    1787             : /*                           ICreateLayer()                             */
    1788             : /************************************************************************/
    1789             : 
    1790             : OGRLayer *
    1791          87 : OGRVDVDataSource::ICreateLayer(const char *pszLayerName,
    1792             :                                const OGRGeomFieldDefn *poGeomFieldDefn,
    1793             :                                CSLConstList papszOptions)
    1794             : {
    1795          87 :     if (!m_bUpdate)
    1796           0 :         return nullptr;
    1797             : 
    1798             :     const char *pszProfile =
    1799          87 :         CSLFetchNameValueDef(papszOptions, "PROFILE", "GENERIC");
    1800          87 :     if (STARTS_WITH_CI(pszProfile, "VDV-452") && !m_bVDV452Loaded)
    1801             :     {
    1802           7 :         m_bVDV452Loaded = true;
    1803           7 :         OGRVDVLoadVDV452Tables(m_oVDV452Tables);
    1804             :     }
    1805             :     const bool bProfileStrict =
    1806          87 :         CPLFetchBool(papszOptions, "PROFILE_STRICT", false);
    1807             :     const bool bCreateAllFields =
    1808          87 :         CPLFetchBool(papszOptions, "CREATE_ALL_FIELDS", true);
    1809             : 
    1810         174 :     CPLString osUpperLayerName(pszLayerName);
    1811          87 :     osUpperLayerName.toupper();
    1812             : 
    1813          87 :     OGRVDV452Table *poVDV452Table = nullptr;
    1814         174 :     CPLString osVDV452Lang;
    1815          87 :     bool bOKTable = true;
    1816          87 :     if (EQUAL(pszProfile, "VDV-452"))
    1817             :     {
    1818           4 :         if (m_oVDV452Tables.oMapEnglish.find(osUpperLayerName) !=
    1819           8 :             m_oVDV452Tables.oMapEnglish.end())
    1820             :         {
    1821           2 :             poVDV452Table = m_oVDV452Tables.oMapEnglish[osUpperLayerName];
    1822           2 :             osVDV452Lang = "en";
    1823             :         }
    1824           2 :         else if (m_oVDV452Tables.oMapGerman.find(osUpperLayerName) !=
    1825           4 :                  m_oVDV452Tables.oMapGerman.end())
    1826             :         {
    1827           1 :             poVDV452Table = m_oVDV452Tables.oMapGerman[osUpperLayerName];
    1828           1 :             osVDV452Lang = "de";
    1829             :         }
    1830             :         else
    1831             :         {
    1832           1 :             bOKTable = false;
    1833             :         }
    1834             :     }
    1835          83 :     else if (EQUAL(pszProfile, "VDV-452-ENGLISH"))
    1836             :     {
    1837           3 :         if (m_oVDV452Tables.oMapEnglish.find(osUpperLayerName) !=
    1838           6 :             m_oVDV452Tables.oMapEnglish.end())
    1839             :         {
    1840           2 :             poVDV452Table = m_oVDV452Tables.oMapEnglish[osUpperLayerName];
    1841           2 :             osVDV452Lang = "en";
    1842             :         }
    1843             :         else
    1844             :         {
    1845           1 :             bOKTable = false;
    1846             :         }
    1847             :     }
    1848          80 :     else if (EQUAL(pszProfile, "VDV-452-GERMAN"))
    1849             :     {
    1850           3 :         if (m_oVDV452Tables.oMapGerman.find(osUpperLayerName) !=
    1851           6 :             m_oVDV452Tables.oMapGerman.end())
    1852             :         {
    1853           2 :             poVDV452Table = m_oVDV452Tables.oMapGerman[osUpperLayerName];
    1854           2 :             osVDV452Lang = "de";
    1855             :         }
    1856             :         else
    1857             :         {
    1858           1 :             bOKTable = false;
    1859             :         }
    1860             :     }
    1861          87 :     if (!bOKTable)
    1862             :     {
    1863           3 :         CPLError(bProfileStrict ? CE_Failure : CE_Warning, CPLE_AppDefined,
    1864             :                  "%s is not a VDV-452 table", pszLayerName);
    1865           3 :         if (bProfileStrict)
    1866           1 :             return nullptr;
    1867             :     }
    1868             : 
    1869          86 :     VSILFILE *fpL = nullptr;
    1870          86 :     if (m_bSingleFile)
    1871             :     {
    1872          77 :         fpL = m_fpL;
    1873          77 :         if (!m_bNew && m_nLayerCount == 0)
    1874             :         {
    1875             :             // Find last non-empty line in the file
    1876           4 :             VSIFSeekL(fpL, 0, SEEK_END);
    1877           4 :             vsi_l_offset nFileSize = VSIFTellL(fpL);
    1878           4 :             vsi_l_offset nOffset = nFileSize;
    1879           4 :             bool bTerminatingEOL = true;
    1880          35 :             while (nOffset > 0)
    1881             :             {
    1882          35 :                 VSIFSeekL(fpL, nOffset - 1, SEEK_SET);
    1883          35 :                 char ch = '\0';
    1884          35 :                 VSIFReadL(&ch, 1, 1, fpL);
    1885          35 :                 if (bTerminatingEOL)
    1886             :                 {
    1887           7 :                     if (!(ch == '\r' || ch == '\n'))
    1888             :                     {
    1889           4 :                         bTerminatingEOL = false;
    1890             :                     }
    1891             :                 }
    1892             :                 else
    1893             :                 {
    1894          28 :                     if (ch == '\r' || ch == '\n')
    1895             :                         break;
    1896             :                 }
    1897          31 :                 nOffset--;
    1898             :             }
    1899             : 
    1900             :             // If it is "eof;..." then overwrite it with new content
    1901           4 :             const char *pszLine = CPLReadLineL(fpL);
    1902           4 :             if (pszLine != nullptr && STARTS_WITH(pszLine, "eof;"))
    1903             :             {
    1904           3 :                 VSIFSeekL(fpL, nOffset, SEEK_SET);
    1905           3 :                 VSIFTruncateL(fpL, VSIFTellL(fpL));
    1906             :             }
    1907           1 :             else if (nFileSize > 0)
    1908             :             {
    1909             :                 // Otherwise make sure the file ends with an eol character
    1910           1 :                 VSIFSeekL(fpL, nFileSize - 1, SEEK_SET);
    1911           1 :                 char ch = '\0';
    1912           1 :                 VSIFReadL(&ch, 1, 1, fpL);
    1913           1 :                 VSIFSeekL(fpL, nFileSize, SEEK_SET);
    1914           1 :                 if (!(ch == '\r' || ch == '\n'))
    1915             :                 {
    1916           0 :                     ch = '\n';
    1917           0 :                     VSIFWriteL(&ch, 1, 1, fpL);
    1918             :                 }
    1919             :             }
    1920             :         }
    1921             :     }
    1922             :     else
    1923             :     {
    1924             :         CPLString osExtension =
    1925           9 :             CSLFetchNameValueDef(papszOptions, "EXTENSION", "x10");
    1926             :         const CPLString osFilename =
    1927           9 :             CPLFormFilenameSafe(m_osFilename, pszLayerName, osExtension);
    1928           9 :         fpL = VSIFOpenL(osFilename, "wb");
    1929           9 :         if (fpL == nullptr)
    1930             :         {
    1931           1 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
    1932             :                      osFilename.c_str());
    1933           1 :             return nullptr;
    1934             :         }
    1935             :     }
    1936             : 
    1937          85 :     GetLayerCount();
    1938             : 
    1939          85 :     if (m_nLayerCount == 0 || !m_bSingleFile)
    1940             :     {
    1941          52 :         if (!OGRVDVWriteHeader(fpL, papszOptions))
    1942             :         {
    1943           0 :             if (!m_bSingleFile)
    1944           0 :                 VSIFCloseL(fpL);
    1945           0 :             return nullptr;
    1946             :         }
    1947             :     }
    1948             : 
    1949          85 :     m_bMustWriteEof = true;
    1950             : 
    1951             :     OGRVDVWriterLayer *poLayer =
    1952          85 :         new OGRVDVWriterLayer(this, pszLayerName, fpL, !m_bSingleFile,
    1953          85 :                               poVDV452Table, osVDV452Lang, bProfileStrict);
    1954          85 :     m_papoLayers = static_cast<OGRLayer **>(
    1955          85 :         CPLRealloc(m_papoLayers, sizeof(OGRLayer *) * (m_nLayerCount + 1)));
    1956          85 :     m_papoLayers[m_nLayerCount] = poLayer;
    1957          85 :     m_nLayerCount++;
    1958             : 
    1959          85 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    1960          85 :     if (eGType == wkbPoint && poVDV452Table != nullptr &&
    1961           4 :         (EQUAL(pszLayerName, "STOP") || EQUAL(pszLayerName, "REC_ORT")))
    1962             :     {
    1963           4 :         poLayer->GetLayerDefn()->SetGeomType(wkbPoint);
    1964             :     }
    1965             : 
    1966          85 :     if (bCreateAllFields && poVDV452Table != nullptr)
    1967             :     {
    1968         126 :         for (size_t i = 0; i < poVDV452Table->aosFields.size(); i++)
    1969             :         {
    1970             :             const char *pszFieldName =
    1971         119 :                 (osVDV452Lang == "en")
    1972         119 :                     ? poVDV452Table->aosFields[i].osEnglishName.c_str()
    1973          51 :                     : poVDV452Table->aosFields[i].osGermanName.c_str();
    1974         119 :             OGRFieldType eType = OFTString;
    1975         119 :             int nWidth = poVDV452Table->aosFields[i].nWidth;
    1976         147 :             if (poVDV452Table->aosFields[i].osType == "num" ||
    1977          28 :                 poVDV452Table->aosFields[i].osType == "boolean")
    1978          91 :                 eType = OFTInteger;
    1979         119 :             if (poVDV452Table->aosFields[i].osType == "num")
    1980             :             {
    1981             :                 /* VDV 451 is without sign */
    1982          91 :                 nWidth++;
    1983          91 :                 if (nWidth >= 10)
    1984          42 :                     eType = OFTInteger64;
    1985             :             }
    1986         238 :             OGRFieldDefn oField(pszFieldName, eType);
    1987         119 :             if (poVDV452Table->aosFields[i].osType == "boolean")
    1988           0 :                 oField.SetSubType(OFSTBoolean);
    1989         119 :             oField.SetWidth(nWidth);
    1990         119 :             poLayer->CreateField(&oField);
    1991             :         }
    1992             :     }
    1993             : 
    1994          85 :     return poLayer;
    1995             : }
    1996             : 
    1997             : /************************************************************************/
    1998             : /*                       SetCurrentWriterLayer()                        */
    1999             : /************************************************************************/
    2000             : 
    2001         130 : void OGRVDVDataSource::SetCurrentWriterLayer(OGRVDVWriterLayer *poLayer)
    2002             : {
    2003         130 :     if (!m_bSingleFile)
    2004          14 :         return;
    2005         116 :     if (m_poCurrentWriterLayer != nullptr && m_poCurrentWriterLayer != poLayer)
    2006             :     {
    2007          21 :         m_poCurrentWriterLayer->StopAsCurrentLayer();
    2008             :     }
    2009         116 :     m_poCurrentWriterLayer = poLayer;
    2010             : }
    2011             : 
    2012             : /************************************************************************/
    2013             : /*                           TestCapability()                           */
    2014             : /************************************************************************/
    2015             : 
    2016          87 : int OGRVDVDataSource::TestCapability(const char *pszCap)
    2017             : 
    2018             : {
    2019          87 :     if (EQUAL(pszCap, ODsCCreateLayer))
    2020          35 :         return m_bUpdate;
    2021          52 :     else if (EQUAL(pszCap, ODsCZGeometries))
    2022          16 :         return true;
    2023             : 
    2024          36 :     return false;
    2025             : }
    2026             : 
    2027             : /************************************************************************/
    2028             : /*                                 Create()                             */
    2029             : /************************************************************************/
    2030             : 
    2031          50 : GDALDataset *OGRVDVDataSource::Create(const char *pszName, int /*nXSize*/,
    2032             :                                       int /*nYSize*/, int /*nBands*/,
    2033             :                                       GDALDataType /*eType*/,
    2034             :                                       char **papszOptions)
    2035             : 
    2036             : {
    2037             :     /* -------------------------------------------------------------------- */
    2038             :     /*      First, ensure there isn't any such file yet.                    */
    2039             :     /* -------------------------------------------------------------------- */
    2040             :     VSIStatBufL sStatBuf;
    2041          50 :     if (VSIStatL(pszName, &sStatBuf) == 0)
    2042             :     {
    2043           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2044             :                  "It seems a file system object called '%s' already exists.",
    2045             :                  pszName);
    2046             : 
    2047           1 :         return nullptr;
    2048             :     }
    2049             : 
    2050          49 :     const bool bSingleFile = CPLFetchBool(papszOptions, "SINGLE_FILE", true);
    2051          49 :     if (!bSingleFile)
    2052             :     {
    2053           3 :         if (VSIMkdir(pszName, 0755) != 0)
    2054             :         {
    2055           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2056             :                      "Failed to create directory %s:\n%s", pszName,
    2057           1 :                      VSIStrerror(errno));
    2058           1 :             return nullptr;
    2059             :         }
    2060             :     }
    2061             : 
    2062          48 :     VSILFILE *fpL = nullptr;
    2063          48 :     if (bSingleFile)
    2064             :     {
    2065          46 :         fpL = VSIFOpenL(pszName, "wb");
    2066          46 :         if (fpL == nullptr)
    2067             :         {
    2068           2 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszName);
    2069           2 :             return nullptr;
    2070             :         }
    2071             :     }
    2072             :     OGRVDVDataSource *poDS =
    2073          46 :         new OGRVDVDataSource(pszName, fpL, true, bSingleFile, true /* new */);
    2074          46 :     return poDS;
    2075             : }
    2076             : 
    2077             : /************************************************************************/
    2078             : /*                         RegisterOGRVDV()                             */
    2079             : /************************************************************************/
    2080             : 
    2081        1686 : void RegisterOGRVDV()
    2082             : 
    2083             : {
    2084        1686 :     if (GDALGetDriverByName("VDV") != nullptr)
    2085         302 :         return;
    2086             : 
    2087        1384 :     GDALDriver *poDriver = new GDALDriver();
    2088             : 
    2089        1384 :     poDriver->SetDescription("VDV");
    2090        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    2091        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
    2092        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
    2093        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
    2094        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
    2095        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
    2096        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
    2097        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
    2098        1384 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
    2099        1384 :                               "WidthPrecision");
    2100        1384 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
    2101        1384 :                               "Name Type WidthPrecision");
    2102        1384 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
    2103             : 
    2104        1384 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    2105        1384 :                               "VDV-451/VDV-452/INTREST Data Format");
    2106        1384 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/vdv.html");
    2107        1384 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "txt x10");
    2108        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    2109        1384 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
    2110        1384 :                               "Integer Integer64 String");
    2111             : 
    2112        1384 :     poDriver->SetMetadataItem(
    2113             :         GDAL_DMD_CREATIONOPTIONLIST,
    2114             :         "<CreationOptionList>"
    2115             :         "  <Option name='SINGLE_FILE' type='boolean' description='Whether "
    2116             :         "several layers "
    2117             :         "should be put in the same file. If no, the name is assumed to be a "
    2118             :         "directory name' default='YES'/>"
    2119        1384 :         "</CreationOptionList>");
    2120             : 
    2121        1384 :     poDriver->SetMetadataItem(
    2122             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
    2123             :         "<LayerCreationOptionList>"
    2124             :         "  <Option name='EXTENSION' type='string' description='Layer file "
    2125             :         "extension. Only used for SINGLE_FILE=NO' default='x10'/>"
    2126             :         "  <Option name='PROFILE' type='string-select' description='Profile' "
    2127             :         "default='GENERIC'>"
    2128             :         "       <Value>GENERIC</Value>"
    2129             :         "       <Value>VDV-452</Value>"
    2130             :         "       <Value>VDV-452-ENGLISH</Value>"
    2131             :         "       <Value>VDV-452-GERMAN</Value>"
    2132             :         "  </Option>"
    2133             :         "  <Option name='PROFILE_STRICT' type='boolean' description='Whether "
    2134             :         "checks of profile should be strict' default='NO'/>"
    2135             :         "  <Option name='CREATE_ALL_FIELDS' type='boolean' description="
    2136             :         "'Whether all fields of predefined profiles should be created at layer "
    2137             :         "creation' default='YES'/>"
    2138             :         "  <Option name='STANDARD_HEADER' type='boolean' description='Whether "
    2139             :         "to write standard header fields' default='YES'/>"
    2140             :         "  <Option name='HEADER_SRC' type='string' description='Value of the "
    2141             :         "src header field' default='UNKNOWN'/>"
    2142             :         "  <Option name='HEADER_SRC_DATE' type='string' description='Value of "
    2143             :         "the date of the src header field as DD.MM.YYYY'/>"
    2144             :         "  <Option name='HEADER_SRC_TIME' type='string' description='Value of "
    2145             :         "the time of the src header field as HH.MM.SS'/>"
    2146             :         "  <Option name='HEADER_CHS' type='string' description='Value of the "
    2147             :         "chs header field' default='ISO8859-1'/>"
    2148             :         "  <Option name='HEADER_VER' type='string' description='Value of the "
    2149             :         "ver header field' default='1.4'/>"
    2150             :         "  <Option name='HEADER_IFV' type='string' description='Value of the "
    2151             :         "ifv header field' default='1.4'/>"
    2152             :         "  <Option name='HEADER_DVE' type='string' description='Value of the "
    2153             :         "dve header field' default='1.4'/>"
    2154             :         "  <Option name='HEADER_FFT' type='string' description='Value of the "
    2155             :         "fft header field' default=''/>"
    2156             :         "  <Option name='HEADER_*' type='string' description='Value of another "
    2157             :         "header field'/>"
    2158        1384 :         "</LayerCreationOptionList>");
    2159        1384 :     poDriver->pfnIdentify = OGRVDVDriverIdentify;
    2160        1384 :     poDriver->pfnOpen = OGRVDVDataSource::Open;
    2161        1384 :     poDriver->pfnCreate = OGRVDVDataSource::Create;
    2162             : 
    2163        1384 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2164             : }

Generated by: LCOV version 1.14