Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/selafin/ogrselafindatasource.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  Selafin importer
3
 * Purpose:  Implementation of OGRSelafinDataSource class.
4
 * Author:   François Hissel, francois.hissel@gmail.com
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2014,  François Hissel <francois.hissel@gmail.com>
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
12
#include "ogr_selafin.h"
13
#include "cpl_conv.h"
14
#include "cpl_string.h"
15
#include "cpl_vsi_virtual.h"
16
#include "cpl_vsi.h"
17
#include "io_selafin.h"
18
19
#include <algorithm>
20
#include <ctime>
21
22
/************************************************************************/
23
/*                                Range                                 */
24
/************************************************************************/
25
Range::~Range()
26
36.8k
{
27
36.8k
    deleteList(poVals);
28
36.8k
    deleteList(poActual);
29
36.8k
}
30
31
void Range::deleteList(Range::List *poList)
32
79.3k
{
33
79.3k
    if (poList == nullptr)
34
78.6k
        return;
35
724
    Range::List *pol = poList;
36
8.46k
    while (pol != nullptr)
37
7.74k
    {
38
7.74k
        poList = poList->poNext;
39
7.74k
        delete pol;
40
7.74k
        pol = poList;
41
7.74k
    }
42
724
}
43
44
void Range::setRange(const char *pszStr)
45
1.94k
{
46
1.94k
    deleteList(poVals);
47
1.94k
    deleteList(poActual);
48
1.94k
    poVals = nullptr;
49
1.94k
    Range::List *poEnd = nullptr;
50
1.94k
    if (pszStr == nullptr || pszStr[0] != '[')
51
0
    {
52
0
        CPLError(CE_Warning, CPLE_IllegalArg, "Invalid range specified");
53
0
        return;
54
0
    }
55
1.94k
    const char *pszc = pszStr;
56
1.94k
    char *psze = nullptr;
57
1.94k
    SelafinTypeDef eType;
58
6.05k
    while (*pszc != 0 && *pszc != ']')
59
6.01k
    {
60
6.01k
        pszc++;
61
6.01k
        if (*pszc == 'p' || *pszc == 'P')
62
532
        {
63
532
            eType = POINTS;
64
532
            pszc++;
65
532
        }
66
5.48k
        else if (*pszc == 'e' || *pszc == 'E')
67
347
        {
68
347
            eType = ELEMENTS;
69
347
            pszc++;
70
347
        }
71
5.13k
        else
72
5.13k
            eType = ALL;
73
74
6.01k
        int nMin = 0;
75
6.01k
        if (*pszc != ':')
76
5.94k
        {
77
5.94k
            nMin = (int)strtol(pszc, &psze, 10);
78
5.94k
            if (*psze != ':' && *psze != ',' && *psze != ']')
79
1.80k
            {
80
1.80k
                CPLError(CE_Warning, CPLE_IllegalArg,
81
1.80k
                         "Invalid range specified");
82
1.80k
                deleteList(poVals);
83
1.80k
                poVals = nullptr;
84
1.80k
                return;
85
1.80k
            }
86
4.14k
            pszc = psze;
87
4.14k
        }
88
4.20k
        int nMax = -1;
89
4.20k
        if (*pszc == ':')
90
200
        {
91
200
            ++pszc;
92
200
            if (*pszc != ',' && *pszc != ']')
93
162
            {
94
162
                nMax = (int)strtol(pszc, &psze, 10);
95
162
                if (*psze != ',' && *psze != ']')
96
91
                {
97
91
                    CPLError(CE_Warning, CPLE_IllegalArg,
98
91
                             "Invalid range specified");
99
91
                    deleteList(poVals);
100
91
                    poVals = nullptr;
101
91
                    return;
102
91
                }
103
71
                pszc = psze;
104
71
            }
105
200
        }
106
4.00k
        else
107
4.00k
            nMax = nMin;
108
4.11k
        Range::List *poNew = nullptr;
109
4.11k
        if (eType != ALL)
110
490
            poNew = new Range::List(eType, nMin, nMax, nullptr);
111
3.62k
        else
112
3.62k
            poNew =
113
3.62k
                new Range::List(POINTS, nMin, nMax,
114
3.62k
                                new Range::List(ELEMENTS, nMin, nMax, nullptr));
115
4.11k
        if (poVals == nullptr)
116
724
        {
117
724
            poVals = poNew;
118
724
            poEnd = poNew;
119
724
        }
120
3.39k
        else
121
3.39k
        {
122
3.39k
            poEnd->poNext = poNew;
123
3.39k
            poEnd = poNew;
124
3.39k
        }
125
4.11k
        if (poEnd->poNext != nullptr)
126
3.62k
            poEnd = poEnd->poNext;
127
4.11k
    }
128
43
    if (*pszc != ']')
129
0
    {
130
0
        CPLError(CE_Warning, CPLE_IllegalArg, "Invalid range specified");
131
0
        deleteList(poVals);
132
0
        poVals = nullptr;
133
0
    }
134
43
}
135
136
bool Range::contains(SelafinTypeDef eType, int nValue) const
137
21.3k
{
138
21.3k
    if (poVals == nullptr)
139
21.3k
        return true;
140
0
    Range::List *poCur = poActual;
141
0
    while (poCur != nullptr)
142
0
    {
143
0
        if (poCur->eType == eType && nValue >= poCur->nMin &&
144
0
            nValue <= poCur->nMax)
145
0
            return true;
146
0
        poCur = poCur->poNext;
147
0
    }
148
0
    return false;
149
0
}
150
151
void Range::sortList(Range::List *&poList, Range::List *poEnd)
152
0
{
153
0
    if (poList == nullptr || poList == poEnd)
154
0
        return;
155
0
    Range::List *pol = poList;
156
0
    Range::List *poBefore = nullptr;
157
0
    Range::List *poBeforeEnd = nullptr;
158
    // poList plays the role of the pivot value. Values greater and smaller are
159
    // sorted on each side of it. The order relation here is POINTS ranges
160
    // first, then sorted by nMin value.
161
0
    while (pol->poNext != poEnd)
162
0
    {
163
0
        if ((pol->eType == ELEMENTS &&
164
0
             (pol->poNext->eType == POINTS || pol->poNext->nMin < pol->nMin)) ||
165
0
            (pol->eType == POINTS && pol->poNext->eType == POINTS &&
166
0
             pol->poNext->nMin < pol->nMin))
167
0
        {
168
0
            if (poBefore == nullptr)
169
0
            {
170
0
                poBefore = pol->poNext;
171
0
                poBeforeEnd = poBefore;
172
0
            }
173
0
            else
174
0
            {
175
0
                poBeforeEnd->poNext = pol->poNext;
176
0
                poBeforeEnd = poBeforeEnd->poNext;
177
0
            }
178
0
            pol->poNext = pol->poNext->poNext;
179
0
        }
180
0
        else
181
0
            pol = pol->poNext;
182
0
    }
183
0
    if (poBefore != nullptr)
184
0
        poBeforeEnd->poNext = poList;
185
    // Now, poList is well placed. We do the same for the sublists before and
186
    // after poList
187
0
    Range::sortList(poBefore, poList);
188
0
    Range::sortList(poList->poNext, poEnd);
189
    // Finally, we restore the right starting point of the list
190
0
    if (poBefore != nullptr)
191
0
        poList = poBefore;
192
0
}
193
194
void Range::setMaxValue(int nMaxValueP)
195
48
{
196
48
    nMaxValue = nMaxValueP;
197
48
    if (poVals == nullptr)
198
48
        return;
199
    // We keep an internal private copy of the list where the range is
200
    // "resolved", that is simplified to a union of disjoint intervals
201
0
    deleteList(poActual);
202
0
    poActual = nullptr;
203
0
    Range::List *pol = poVals;
204
0
    Range::List *poActualEnd = nullptr;
205
0
    int nMinT, nMaxT;
206
0
    while (pol != nullptr)
207
0
    {
208
0
        if (pol->nMin < 0)
209
0
            nMinT = pol->nMin + nMaxValue;
210
0
        else
211
0
            nMinT = pol->nMin;
212
0
        if (pol->nMin < 0)
213
0
            pol->nMin = 0;
214
0
        if (pol->nMin >= nMaxValue)
215
0
            pol->nMin = nMaxValue - 1;
216
0
        if (pol->nMax < 0)
217
0
            nMaxT = pol->nMax + nMaxValue;
218
0
        else
219
0
            nMaxT = pol->nMax;
220
0
        if (pol->nMax < 0)
221
0
            pol->nMax = 0;
222
0
        if (pol->nMax >= nMaxValue)
223
0
            pol->nMax = nMaxValue - 1;
224
0
        if (nMaxT < nMinT)
225
0
            continue;
226
0
        if (poActual == nullptr)
227
0
        {
228
0
            poActual = new Range::List(pol->eType, nMinT, nMaxT, nullptr);
229
0
            poActualEnd = poActual;
230
0
        }
231
0
        else
232
0
        {
233
0
            poActualEnd->poNext =
234
0
                new Range::List(pol->eType, nMinT, nMaxT, nullptr);
235
0
            poActualEnd = poActualEnd->poNext;
236
0
        }
237
0
        pol = pol->poNext;
238
0
    }
239
0
    sortList(poActual);
240
    // Now we merge successive ranges when they intersect or are consecutive
241
0
    if (poActual != nullptr)
242
0
    {
243
0
        pol = poActual;
244
0
        while (pol->poNext != nullptr)
245
0
        {
246
0
            if (pol->poNext->eType == pol->eType &&
247
0
                pol->poNext->nMin <= pol->nMax + 1)
248
0
            {
249
0
                if (pol->poNext->nMax > pol->nMax)
250
0
                    pol->nMax = pol->poNext->nMax;
251
0
                poActualEnd = pol->poNext->poNext;
252
0
                delete pol->poNext;
253
0
                pol->poNext = poActualEnd;
254
0
            }
255
0
            else
256
0
                pol = pol->poNext;
257
0
        }
258
0
    }
259
0
}
260
261
size_t Range::getSize() const
262
48
{
263
48
    if (poVals == nullptr)
264
48
        return nMaxValue * 2;
265
0
    Range::List *pol = poActual;
266
0
    size_t nSize = 0;
267
0
    while (pol != nullptr)
268
0
    {
269
0
        nSize += (pol->nMax - pol->nMin + 1);
270
0
        pol = pol->poNext;
271
0
    }
272
0
    return nSize;
273
48
}
274
275
/************************************************************************/
276
/*                        OGRSelafinDataSource()                        */
277
/************************************************************************/
278
279
OGRSelafinDataSource::OGRSelafinDataSource()
280
36.8k
    : pszName(nullptr), papoLayers(nullptr), nLayers(0), bUpdate(false),
281
36.8k
      poHeader(nullptr), poSpatialRef(nullptr)
282
36.8k
{
283
36.8k
}
284
285
/************************************************************************/
286
/*                       ~OGRSelafinDataSource()                        */
287
/************************************************************************/
288
289
OGRSelafinDataSource::~OGRSelafinDataSource()
290
36.8k
{
291
58.1k
    for (int i = 0; i < nLayers; i++)
292
21.3k
        delete papoLayers[i];
293
36.8k
    CPLFree(papoLayers);
294
36.8k
    CPLFree(pszName);
295
36.8k
    delete poHeader;
296
36.8k
    if (poSpatialRef != nullptr)
297
10
        poSpatialRef->Release();
298
36.8k
}
299
300
/************************************************************************/
301
/*                           TestCapability()                           */
302
/************************************************************************/
303
304
int OGRSelafinDataSource::TestCapability(const char *pszCap) const
305
0
{
306
0
    if (EQUAL(pszCap, ODsCCreateLayer))
307
0
        return TRUE;
308
0
    else if (EQUAL(pszCap, ODsCDeleteLayer))
309
0
        return TRUE;
310
0
    else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
311
0
        return FALSE;
312
0
    else
313
0
        return FALSE;
314
0
}
315
316
/************************************************************************/
317
/*                              GetLayer()                              */
318
/************************************************************************/
319
320
const OGRLayer *OGRSelafinDataSource::GetLayer(int iLayer) const
321
464
{
322
464
    if (iLayer < 0 || iLayer >= nLayers)
323
0
        return nullptr;
324
464
    else
325
464
        return papoLayers[iLayer];
326
464
}
327
328
/************************************************************************/
329
/*                                Open()                                */
330
/************************************************************************/
331
int OGRSelafinDataSource::Open(const char *pszFilename, int bUpdateIn,
332
                               int bCreate)
333
36.8k
{
334
    // Check if a range is set and extract it and the filename.
335
36.8k
    const char *pszc = pszFilename;
336
36.8k
    if (*pszFilename == 0)
337
0
        return FALSE;
338
62.6M
    while (*pszc)
339
62.6M
        ++pszc;
340
36.8k
    if (*(pszc - 1) == ']')
341
2.90k
    {
342
2.90k
        --pszc;
343
2.49M
        while (pszc != pszFilename && *pszc != '[')
344
2.49M
            pszc--;
345
2.90k
        if (pszc == pszFilename)
346
961
            return FALSE;
347
1.94k
        poRange.setRange(pszc);
348
1.94k
    }
349
35.8k
    pszName = CPLStrdup(pszFilename);
350
35.8k
    pszName[pszc - pszFilename] = 0;
351
35.8k
    bUpdate = CPL_TO_BOOL(bUpdateIn);
352
35.8k
    if (bCreate && EQUAL(pszName, "/vsistdout/"))
353
0
        return TRUE;
354
    /* For writable /vsizip/, do nothing more */
355
35.8k
    if (bCreate && STARTS_WITH(pszName, "/vsizip/"))
356
0
        return TRUE;
357
35.8k
    CPLString osFilename(pszName);
358
    // Determine what sort of object this is.
359
35.8k
    VSIStatBufL sStatBuf;
360
35.8k
    if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
361
35.4k
        return FALSE;
362
363
    // Is this a single Selafin file?
364
408
    if (VSI_ISREG(sStatBuf.st_mode))
365
408
        return OpenTable(pszName);
366
367
    // Is this a single a ZIP file with only a Selafin file inside ?
368
0
    if (STARTS_WITH(osFilename, "/vsizip/") && VSI_ISREG(sStatBuf.st_mode))
369
0
    {
370
0
        char **papszFiles = VSIReadDir(osFilename);
371
0
        if (CSLCount(papszFiles) != 1)
372
0
        {
373
0
            CSLDestroy(papszFiles);
374
0
            return FALSE;
375
0
        }
376
0
        osFilename = CPLFormFilenameSafe(osFilename, papszFiles[0], nullptr);
377
0
        CSLDestroy(papszFiles);
378
0
        return OpenTable(osFilename);
379
0
    }
380
381
#ifdef notdef
382
    // Otherwise it has to be a directory.
383
    if (!VSI_ISDIR(sStatBuf.st_mode))
384
        return FALSE;
385
386
    // Scan through for entries which look like Selafin files
387
    int nNotSelafinCount = 0, i;
388
    char **papszNames = VSIReadDir(osFilename);
389
    for (i = 0; papszNames != NULL && papszNames[i] != NULL; i++)
390
    {
391
        const CPLString oSubFilename =
392
            CPLFormFilenameSafe(osFilename, papszNames[i], NULL);
393
        if (EQUAL(papszNames[i], ".") || EQUAL(papszNames[i], ".."))
394
            continue;
395
        if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
396
            !VSI_ISREG(sStatBuf.st_mode))
397
        {
398
            nNotSelafinCount++;
399
            continue;
400
        }
401
        if (!OpenTable(oSubFilename))
402
        {
403
            CPLDebug("Selafin", "Cannot open %s", oSubFilename.c_str());
404
            nNotSelafinCount++;
405
            continue;
406
        }
407
    }
408
    CSLDestroy(papszNames);
409
410
    // We presume that this is indeed intended to be a Selafin datasource if
411
    // over half the files were Selafin files.
412
    return nNotSelafinCount < nLayers;
413
#else
414
0
    return FALSE;
415
0
#endif
416
0
}
417
418
/************************************************************************/
419
/*                             OpenTable()                              */
420
/************************************************************************/
421
int OGRSelafinDataSource::OpenTable(const char *pszFilename)
422
408
{
423
#ifdef DEBUG_VERBOSE
424
    CPLDebug("Selafin", "OpenTable(%s,%i)", pszFilename,
425
             static_cast<int>(bUpdate));
426
#endif
427
    // Open the file
428
408
    VSILFILE *fp = nullptr;
429
408
    if (bUpdate)
430
0
    {
431
0
        fp = VSIFOpenExL(pszFilename, "rb+", true);
432
0
    }
433
408
    else
434
408
    {
435
408
        fp = VSIFOpenExL(pszFilename, "rb", true);
436
408
    }
437
438
408
    if (fp == nullptr)
439
17
    {
440
17
        CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
441
17
                 VSIGetLastErrorMsg());
442
17
        return FALSE;
443
17
    }
444
391
    if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
445
344
        strstr(pszFilename, "/vsizip/") == nullptr)
446
339
        fp = VSICreateBufferedReaderHandle(fp);
447
448
    // Quickly check if the file is in Selafin format, before actually starting
449
    // to read to make it faster
450
391
    char szBuf[9];
451
391
    VSIFReadL(szBuf, 1, 4, fp);
452
391
    if (szBuf[0] != 0 || szBuf[1] != 0 || szBuf[2] != 0 || szBuf[3] != 0x50)
453
129
    {
454
129
        VSIFCloseL(fp);
455
129
        return FALSE;
456
129
    }
457
262
    VSIFSeekL(fp, 84, SEEK_SET);
458
262
    VSIFReadL(szBuf, 1, 8, fp);
459
262
    if (szBuf[0] != 0 || szBuf[1] != 0 || szBuf[2] != 0 || szBuf[3] != 0x50 ||
460
262
        szBuf[4] != 0 || szBuf[5] != 0 || szBuf[6] != 0 || szBuf[7] != 8)
461
0
    {
462
0
        VSIFCloseL(fp);
463
0
        return FALSE;
464
0
    }
465
    /* VSIFSeekL(fp,76,SEEK_SET);
466
    VSIFReadL(szBuf,1,8,fp);
467
    if (STRNCASECMP(szBuf,"Seraphin",8)!=0 && STRNCASECMP(szBuf,"Serafin",7)!=0)
468
    { VSIFCloseL(fp); return FALSE;
469
    } */
470
471
    // Get layer base name
472
262
    CPLString osBaseLayerName = CPLGetBasenameSafe(pszFilename);
473
474
    // Read header of file to get common information for all layers
475
    // poHeader now owns fp
476
262
    poHeader = Selafin::read_header(fp, pszFilename);
477
262
    if (poHeader == nullptr)
478
214
    {
479
214
        CPLError(CE_Failure, CPLE_OpenFailed,
480
214
                 "Failed to open %s, wrong format.\n", pszFilename);
481
214
        return FALSE;
482
214
    }
483
48
    if (poHeader->nEpsg != 0)
484
48
    {
485
48
        poSpatialRef = new OGRSpatialReference();
486
48
        poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
487
48
        if (poSpatialRef->importFromEPSG(poHeader->nEpsg) != OGRERR_NONE)
488
38
        {
489
38
            CPLError(CE_Warning, CPLE_AppDefined,
490
38
                     "EPSG %d not found. Could not set datasource SRS.\n",
491
38
                     poHeader->nEpsg);
492
38
            delete poSpatialRef;
493
38
            poSpatialRef = nullptr;
494
38
        }
495
48
    }
496
497
    // To prevent int overflow in poRange.getSize() call where we do
498
    // nSteps * 2
499
48
    if (poHeader->nSteps >= INT_MAX / 2)
500
0
    {
501
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Invalid nSteps value");
502
0
        return FALSE;
503
0
    }
504
505
    // Create two layers for each selected time step: one for points, the other
506
    // for elements
507
48
    poRange.setMaxValue(poHeader->nSteps);
508
48
    size_t size = poRange.getSize();
509
48
    if (size > INT32_MAX)
510
0
    {
511
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Invalid size");
512
0
        return FALSE;
513
0
    }
514
48
    const int nNewLayers = static_cast<int>(size);
515
48
    if (EQUAL(pszFilename, "/vsistdin/"))
516
0
        osBaseLayerName = "layer";
517
48
    CPLString osLayerName;
518
48
    papoLayers = (OGRSelafinLayer **)CPLRealloc(
519
48
        papoLayers, sizeof(void *) * (nLayers + nNewLayers));
520
144
    for (size_t j = 0; j < 2; ++j)
521
96
    {
522
96
        SelafinTypeDef eType = (j == 0) ? POINTS : ELEMENTS;
523
21.4k
        for (int i = 0; i < poHeader->nSteps; ++i)
524
21.3k
        {
525
21.3k
            if (poRange.contains(eType, i))
526
21.3k
            {
527
21.3k
                char szTemp[30] = {};
528
21.3k
                double dfTime = 0.0;
529
21.3k
                if (VSIFSeekL(fp, poHeader->getPosition(i) + 4, SEEK_SET) !=
530
21.3k
                        0 ||
531
21.3k
                    Selafin::read_float(fp, dfTime) == 0)
532
0
                {
533
0
                    CPLError(CE_Failure, CPLE_OpenFailed,
534
0
                             "Failed to open %s, wrong format.\n", pszFilename);
535
0
                    return FALSE;
536
0
                }
537
21.3k
                if (poHeader->panStartDate == nullptr)
538
2.78k
                    snprintf(szTemp, 29, "%d", i);
539
18.5k
                else
540
18.5k
                {
541
18.5k
                    struct tm sDate;
542
18.5k
                    memset(&sDate, 0, sizeof(sDate));
543
18.5k
                    sDate.tm_year =
544
18.5k
                        std::max(poHeader->panStartDate[0], 0) - 1900;
545
18.5k
                    sDate.tm_mon = std::max(poHeader->panStartDate[1], 1) - 1;
546
18.5k
                    sDate.tm_mday = poHeader->panStartDate[2];
547
18.5k
                    sDate.tm_hour = poHeader->panStartDate[3];
548
18.5k
                    sDate.tm_min = poHeader->panStartDate[4];
549
18.5k
                    double dfSec = poHeader->panStartDate[5] + dfTime;
550
18.5k
                    if (dfSec >= 0 && dfSec < 60)
551
6.99k
                        sDate.tm_sec = static_cast<int>(dfSec);
552
18.5k
                    mktime(&sDate);
553
18.5k
                    strftime(szTemp, 29, "%Y_%m_%d_%H_%M_%S", &sDate);
554
18.5k
                }
555
21.3k
                if (eType == POINTS)
556
10.6k
                    osLayerName = osBaseLayerName + "_p" + szTemp;
557
10.6k
                else
558
10.6k
                    osLayerName = osBaseLayerName + "_e" + szTemp;
559
21.3k
                papoLayers[nLayers++] =
560
21.3k
                    new OGRSelafinLayer(this, osLayerName, bUpdate,
561
21.3k
                                        poSpatialRef, poHeader, i, eType);
562
                // poHeader->nRefCount++;
563
21.3k
            }
564
21.3k
        }
565
96
    }
566
567
    // Free allocated variables and exit
568
48
    return TRUE;
569
48
}
570
571
/************************************************************************/
572
/*                            ICreateLayer()                            */
573
/************************************************************************/
574
575
OGRLayer *
576
OGRSelafinDataSource::ICreateLayer(const char *pszLayerName,
577
                                   const OGRGeomFieldDefn *poGeomFieldDefn,
578
                                   CSLConstList papszOptions)
579
580
0
{
581
0
    auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
582
0
    const auto poSpatialRefP =
583
0
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
584
585
0
    CPLDebug("Selafin", "CreateLayer(%s,%s)", pszLayerName,
586
0
             (eGType == wkbPoint) ? "wkbPoint" : "wkbPolygon");
587
    // Verify we are in update mode.
588
0
    if (!bUpdate)
589
0
    {
590
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
591
0
                 "Data source %s opened read-only.  "
592
0
                 "New layer %s cannot be created.",
593
0
                 pszName, pszLayerName);
594
0
        return nullptr;
595
0
    }
596
597
    // Check that new layer is a point or polygon layer
598
0
    if (eGType != wkbPoint)
599
0
    {
600
0
        CPLError(
601
0
            CE_Failure, CPLE_NoWriteAccess,
602
0
            "Selafin format can only handle %s layers whereas input is %s\n.",
603
0
            OGRGeometryTypeToName(wkbPoint), OGRGeometryTypeToName(eGType));
604
0
        return nullptr;
605
0
    }
606
    // Parse options
607
0
    const char *pszTemp = CSLFetchNameValue(papszOptions, "DATE");
608
0
    const double dfDate = pszTemp != nullptr ? CPLAtof(pszTemp) : 0.0;
609
    // Set the SRS of the datasource if this is the first layer
610
0
    if (nLayers == 0 && poSpatialRefP != nullptr)
611
0
    {
612
0
        poSpatialRef = poSpatialRefP->Clone();
613
0
        const char *szEpsg = poSpatialRef->GetAttrValue("GEOGCS|AUTHORITY", 1);
614
0
        int nEpsg = 0;
615
0
        if (szEpsg != nullptr)
616
0
            nEpsg = (int)strtol(szEpsg, nullptr, 10);
617
0
        if (nEpsg == 0)
618
0
        {
619
0
            CPLError(CE_Warning, CPLE_AppDefined,
620
0
                     "Could not find EPSG code for SRS. The SRS won't be saved "
621
0
                     "in the datasource.");
622
0
        }
623
0
        else
624
0
        {
625
0
            poHeader->nEpsg = nEpsg;
626
0
        }
627
0
    }
628
    // Create the new layer in the Selafin file by adding a "time step" at the
629
    // end Beware, as the new layer shares the same header, it automatically
630
    // contains the same number of features and fields as the existing ones.
631
    // This may not be intuitive for the user.
632
0
    if (VSIFSeekL(poHeader->fp, 0, SEEK_END) != 0)
633
0
        return nullptr;
634
0
    if (Selafin::write_integer(poHeader->fp, 4) == 0 ||
635
0
        Selafin::write_float(poHeader->fp, dfDate) == 0 ||
636
0
        Selafin::write_integer(poHeader->fp, 4) == 0)
637
0
    {
638
0
        CPLError(CE_Failure, CPLE_FileIO,
639
0
                 "Could not write to Selafin file %s.\n", pszName);
640
0
        return nullptr;
641
0
    }
642
0
    double *pdfValues = nullptr;
643
0
    if (poHeader->nPoints > 0)
644
0
    {
645
0
        pdfValues =
646
0
            (double *)VSI_MALLOC2_VERBOSE(sizeof(double), poHeader->nPoints);
647
0
        if (pdfValues == nullptr)
648
0
            return nullptr;
649
0
    }
650
0
    for (int i = 0; i < poHeader->nVar; ++i)
651
0
    {
652
0
        if (Selafin::write_floatarray(poHeader->fp, pdfValues,
653
0
                                      poHeader->nPoints) == 0)
654
0
        {
655
0
            CPLError(CE_Failure, CPLE_FileIO,
656
0
                     "Could not write to Selafin file %s.\n", pszName);
657
0
            CPLFree(pdfValues);
658
0
            return nullptr;
659
0
        }
660
0
    }
661
0
    CPLFree(pdfValues);
662
0
    VSIFFlushL(poHeader->fp);
663
0
    poHeader->nSteps++;
664
    // Create two layers as usual, one for points and one for elements
665
0
    nLayers += 2;
666
0
    papoLayers =
667
0
        (OGRSelafinLayer **)CPLRealloc(papoLayers, sizeof(void *) * nLayers);
668
0
    const CPLString osName(pszLayerName);
669
0
    CPLString osNewLayerName(osName + "_p");
670
0
    papoLayers[nLayers - 2] =
671
0
        new OGRSelafinLayer(this, osNewLayerName, bUpdate, poSpatialRef,
672
0
                            poHeader, poHeader->nSteps - 1, POINTS);
673
0
    osNewLayerName = osName + "_e";
674
0
    papoLayers[nLayers - 1] =
675
0
        new OGRSelafinLayer(this, osNewLayerName, bUpdate, poSpatialRef,
676
0
                            poHeader, poHeader->nSteps - 1, ELEMENTS);
677
0
    return papoLayers[nLayers - 2];
678
0
}
679
680
/************************************************************************/
681
/*                            DeleteLayer()                             */
682
/************************************************************************/
683
OGRErr OGRSelafinDataSource::DeleteLayer(int iLayer)
684
0
{
685
    // Verify we are in update mode.
686
0
    if (!bUpdate)
687
0
    {
688
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
689
0
                 "Data source %s opened read-only.  "
690
0
                 "Layer %d cannot be deleted.\n",
691
0
                 pszName, iLayer);
692
0
        return OGRERR_FAILURE;
693
0
    }
694
0
    if (iLayer < 0 || iLayer >= nLayers)
695
0
    {
696
0
        CPLError(CE_Failure, CPLE_AppDefined,
697
0
                 "Layer %d not in legal range of 0 to %d.", iLayer,
698
0
                 nLayers - 1);
699
0
        return OGRERR_FAILURE;
700
0
    }
701
    // Delete layer in file. Here we don't need to create a copy of the file
702
    // because we only update values and it can't get corrupted even if the
703
    // system crashes during the operation
704
0
    const int nNum = papoLayers[iLayer]->GetStepNumber();
705
0
    double *dfValues = nullptr;
706
0
    for (int i = nNum; i < poHeader->nSteps - 1; ++i)
707
0
    {
708
0
        double dfTime = 0.0;
709
0
        if (VSIFSeekL(poHeader->fp, poHeader->getPosition(i + 1) + 4,
710
0
                      SEEK_SET) != 0 ||
711
0
            Selafin::read_float(poHeader->fp, dfTime) == 0 ||
712
0
            VSIFSeekL(poHeader->fp, poHeader->getPosition(i) + 4, SEEK_SET) !=
713
0
                0 ||
714
0
            Selafin::write_float(poHeader->fp, dfTime) == 0)
715
0
        {
716
0
            CPLError(CE_Failure, CPLE_FileIO,
717
0
                     "Could not update Selafin file %s.\n", pszName);
718
0
            return OGRERR_FAILURE;
719
0
        }
720
0
        for (int j = 0; j < poHeader->nVar; ++j)
721
0
        {
722
0
            bool ok = true;
723
0
            if (VSIFSeekL(poHeader->fp, poHeader->getPosition(i + 1) + 12,
724
0
                          SEEK_SET) != 0)
725
0
            {
726
0
                ok = false;
727
0
            }
728
0
            else
729
0
            {
730
0
                int ret = Selafin::read_floatarray(poHeader->fp, &dfValues,
731
0
                                                   poHeader->nFileSize);
732
0
                if (ret < 0 || ret != poHeader->nPoints ||
733
0
                    VSIFSeekL(poHeader->fp, poHeader->getPosition(i) + 12,
734
0
                              SEEK_SET) != 0 ||
735
0
                    Selafin::write_floatarray(poHeader->fp, dfValues,
736
0
                                              poHeader->nPoints) == 0)
737
0
                {
738
0
                    ok = false;
739
0
                }
740
0
            }
741
0
            if (!ok)
742
0
            {
743
0
                CPLError(CE_Failure, CPLE_FileIO,
744
0
                         "Could not update Selafin file %s.\n", pszName);
745
0
                CPLFree(dfValues);
746
0
                return OGRERR_FAILURE;
747
0
            }
748
0
            CPLFree(dfValues);
749
0
            dfValues = nullptr;
750
0
        }
751
0
    }
752
    // Delete all layers with the same step number in layer list. Usually there
753
    // are two of them: one for points and one for elements, but we can't rely
754
    // on that because of possible layer filtering specifications
755
0
    for (int i = 0; i < nLayers; ++i)
756
0
    {
757
0
        if (papoLayers[i]->GetStepNumber() == nNum)
758
0
        {
759
0
            delete papoLayers[i];
760
0
            nLayers--;
761
0
            for (int j = i; j < nLayers; ++j)
762
0
                papoLayers[j] = papoLayers[j + 1];
763
0
            --i;
764
0
        }
765
0
    }
766
0
    return OGRERR_NONE;
767
0
}