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

Generated by: LCOV version 1.14