Coverage Report

Created: 2025-07-23 09:13

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