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

Generated by: LCOV version 1.14