Coverage Report

Created: 2026-03-30 09:00

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