Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/vfk/vfkdatablock.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  VFK Reader - Data block definition
4
 * Purpose:  Implements VFKDataBlock class.
5
 * Author:   Martin Landa, landa.martin gmail.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2009-2013, Martin Landa <landa.martin gmail.com>
9
 * Copyright (c) 2012-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include <ctime>
15
16
#include "vfkreader.h"
17
#include "vfkreaderp.h"
18
19
#include "cpl_conv.h"
20
#include "cpl_error.h"
21
22
/*!
23
  \brief VFK Data Block constructor
24
25
  \param pszName data block name
26
*/
27
IVFKDataBlock::IVFKDataBlock(const char *pszName, const IVFKReader *poReader)
28
8.48k
    : m_papoFeature(nullptr), m_nPropertyCount(0), m_papoProperty(nullptr),
29
8.48k
      m_pszName(CPLStrdup(pszName)),
30
8.48k
      m_bGeometry(false),  // Geometry is not loaded by default.
31
8.48k
      m_nGeometryType(wkbUnknown),
32
8.48k
      m_bGeometryPerBlock(true),  // Load geometry per block/feature.
33
8.48k
      m_nFeatureCount(-1),        // Load data on first request.
34
8.48k
      m_iNextFeature(-1), m_poReader(const_cast<IVFKReader *>(poReader))
35
8.48k
{
36
8.48k
    m_nRecordCount[RecordValid] = 0L;    // Number of valid records.
37
8.48k
    m_nRecordCount[RecordSkipped] = 0L;  // Number of skipped (invalid) records.
38
8.48k
    m_nRecordCount[RecordDuplicated] = 0L;  // Number of duplicated records.
39
8.48k
}
40
41
/*!
42
  \brief VFKDataBlock destructor
43
*/
44
IVFKDataBlock::~IVFKDataBlock()
45
8.48k
{
46
8.48k
    CPLFree(m_pszName);
47
48
56.0k
    for (int i = 0; i < m_nPropertyCount; i++)
49
47.5k
    {
50
47.5k
        if (m_papoProperty[i])
51
47.5k
            delete m_papoProperty[i];
52
47.5k
    }
53
8.48k
    CPLFree(m_papoProperty);
54
55
24.8k
    for (int i = 0; i < m_nFeatureCount; i++)
56
16.3k
    {
57
16.3k
        if (m_papoFeature[i])
58
16.3k
            delete m_papoFeature[i];
59
16.3k
    }
60
8.48k
    CPLFree(m_papoFeature);
61
8.48k
}
62
63
/*!
64
  \brief Get property definition
65
66
  \param iIndex property index
67
68
  \return pointer to VFKPropertyDefn definition or NULL on failure
69
*/
70
VFKPropertyDefn *IVFKDataBlock::GetProperty(int iIndex) const
71
721k
{
72
721k
    if (iIndex < 0 || iIndex >= m_nPropertyCount)
73
0
        return nullptr;
74
75
721k
    return m_papoProperty[iIndex];
76
721k
}
77
78
/*!
79
  \brief Set properties
80
81
  \param poLine pointer to line
82
*/
83
void IVFKDataBlock::SetProperties(const char *poLine)
84
8.48k
{
85
    /* skip data block name */
86
8.48k
    const char *poChar = strchr(poLine, ';');
87
8.48k
    if (poChar == nullptr)
88
31
        return;
89
90
8.45k
    poChar++;
91
92
    /* read property name/type */
93
8.45k
    const char *poProp = poChar;
94
8.45k
    char *pszName = nullptr;
95
8.45k
    char *pszType = nullptr;
96
8.45k
    int nLength = 0;
97
844k
    while (*poChar != '\0')
98
837k
    {
99
837k
        if (*poChar == ' ')
100
61.5k
        {
101
61.5k
            pszName = (char *)CPLRealloc(pszName, nLength + 1);
102
61.5k
            strncpy(pszName, poProp, nLength);
103
61.5k
            pszName[nLength] = '\0';
104
105
61.5k
            poProp = ++poChar;
106
61.5k
            nLength = 0;
107
61.5k
            if (*poProp == '\0')
108
589
                break;
109
61.5k
        }
110
775k
        else if (*poChar == ';')
111
46.8k
        {
112
46.8k
            pszType = (char *)CPLRealloc(pszType, nLength + 1);
113
46.8k
            strncpy(pszType, poProp, nLength);
114
46.8k
            pszType[nLength] = '\0';
115
116
            /* add property */
117
46.8k
            if (pszName && *pszName != '\0' && *pszType != '\0')
118
42.9k
                AddProperty(pszName, pszType);
119
120
46.8k
            poProp = ++poChar;
121
46.8k
            nLength = 0;
122
46.8k
            if (*poProp == '\0')
123
620
                break;
124
46.8k
        }
125
835k
        poChar++;
126
835k
        nLength++;
127
835k
    }
128
129
8.45k
    pszType = (char *)CPLRealloc(pszType, nLength + 1);
130
8.45k
    if (nLength > 0)
131
6.15k
        strncpy(pszType, poProp, nLength);
132
8.45k
    pszType[nLength] = '\0';
133
134
    /* add property */
135
8.45k
    if (pszName && *pszName != '\0' && *pszType != '\0')
136
4.60k
        AddProperty(pszName, pszType);
137
138
8.45k
    CPLFree(pszName);
139
8.45k
    CPLFree(pszType);
140
8.45k
}
141
142
/*!
143
  \brief Add data block property
144
145
  \param pszName property name
146
  \param pszType property type
147
148
  \return number of properties
149
*/
150
int IVFKDataBlock::AddProperty(const char *pszName, const char *pszType)
151
47.5k
{
152
    /* Force text attributes to avoid int64 overflow
153
       see https://github.com/OSGeo/gdal/issues/672 */
154
47.5k
    if (EQUAL(m_pszName, "VLA") &&
155
84
        (EQUAL(pszName, "PODIL_CITATEL") || EQUAL(pszName, "PODIL_JMENOVATEL")))
156
14
        pszType = "T30";
157
158
47.5k
    VFKPropertyDefn *poNewProperty =
159
47.5k
        new VFKPropertyDefn(pszName, pszType, m_poReader->GetEncoding());
160
161
47.5k
    m_nPropertyCount++;
162
163
47.5k
    m_papoProperty = (VFKPropertyDefn **)CPLRealloc(
164
47.5k
        m_papoProperty, sizeof(VFKPropertyDefn *) * m_nPropertyCount);
165
47.5k
    m_papoProperty[m_nPropertyCount - 1] = poNewProperty;
166
167
47.5k
    return m_nPropertyCount;
168
47.5k
}
169
170
/*!
171
  \brief Get number of features for given data block
172
173
  \param bForce true to force reading VFK data blocks if needed
174
175
  \return number of features
176
*/
177
GIntBig IVFKDataBlock::GetFeatureCount(bool bForce)
178
162k
{
179
162k
    if (bForce && m_nFeatureCount == -1)
180
0
    {
181
0
        m_poReader->ReadDataRecords(this); /* read VFK data records */
182
0
        if (m_bGeometryPerBlock && !m_bGeometry)
183
0
        {
184
0
            LoadGeometry(); /* get real number of features */
185
0
        }
186
0
    }
187
162k
#if defined(__GNUC__)
188
162k
#pragma GCC diagnostic push
189
162k
#pragma GCC diagnostic ignored "-Wnull-dereference"
190
162k
#endif
191
162k
    return m_nFeatureCount;
192
162k
#if defined(__GNUC__)
193
162k
#pragma GCC diagnostic pop
194
162k
#endif
195
162k
}
196
197
/*!
198
  \brief Set number of features per data block
199
200
  \param nNewCount number of features
201
  \param bIncrement increment current value
202
*/
203
void IVFKDataBlock::SetFeatureCount(int nNewCount, bool bIncrement)
204
8.48k
{
205
8.48k
    if (bIncrement)
206
0
    {
207
0
        m_nFeatureCount += nNewCount;
208
0
    }
209
8.48k
    else
210
8.48k
    {
211
8.48k
        m_nFeatureCount = nNewCount;
212
8.48k
    }
213
8.48k
}
214
215
/*!
216
  \brief Reset reading
217
218
  \param iIdx force index
219
*/
220
void IVFKDataBlock::ResetReading(int iIdx)
221
4.49k
{
222
4.49k
    if (iIdx > -1)
223
0
    {
224
0
        m_iNextFeature = iIdx;
225
0
    }
226
4.49k
    else
227
4.49k
    {
228
4.49k
        m_iNextFeature = 0;
229
4.49k
    }
230
4.49k
}
231
232
/*!
233
  \brief Get next feature
234
235
  \return pointer to VFKFeature instance or NULL on error
236
*/
237
IVFKFeature *IVFKDataBlock::GetNextFeature()
238
17.4k
{
239
17.4k
    if (m_nFeatureCount < 0)
240
0
    {
241
0
        m_poReader->ReadDataRecords(this);
242
0
    }
243
244
17.4k
    if (m_bGeometryPerBlock && !m_bGeometry)
245
0
    {
246
0
        LoadGeometry();
247
0
    }
248
249
17.4k
    if (m_iNextFeature < 0)
250
4.49k
        ResetReading();
251
252
17.4k
    if (m_iNextFeature < 0 || m_iNextFeature >= m_nFeatureCount)
253
4.49k
        return nullptr;
254
255
12.9k
    return m_papoFeature[m_iNextFeature++];
256
17.4k
}
257
258
/*!
259
  \brief Get previous feature
260
261
  \return pointer to VFKFeature instance or NULL on error
262
*/
263
IVFKFeature *IVFKDataBlock::GetPreviousFeature()
264
0
{
265
0
    if (m_nFeatureCount < 0)
266
0
    {
267
0
        m_poReader->ReadDataRecords(this);
268
0
    }
269
270
0
    if (m_bGeometryPerBlock && !m_bGeometry)
271
0
    {
272
0
        LoadGeometry();
273
0
    }
274
275
0
    if (m_iNextFeature < 0)
276
0
        ResetReading();
277
278
0
    if (m_iNextFeature < 0 || m_iNextFeature >= m_nFeatureCount)
279
0
        return nullptr;
280
281
0
    return m_papoFeature[m_iNextFeature--];
282
0
}
283
284
/*!
285
  \brief Get first feature
286
287
  \return pointer to VFKFeature instance or NULL on error
288
*/
289
IVFKFeature *IVFKDataBlock::GetFirstFeature()
290
0
{
291
0
    if (m_nFeatureCount < 0)
292
0
    {
293
0
        m_poReader->ReadDataRecords(this);
294
0
    }
295
296
0
    if (m_bGeometryPerBlock && !m_bGeometry)
297
0
    {
298
0
        LoadGeometry();
299
0
    }
300
301
0
    if (m_nFeatureCount < 1)
302
0
        return nullptr;
303
304
0
    return m_papoFeature[0];
305
0
}
306
307
/*!
308
  \brief Get last feature
309
310
  \return pointer to VFKFeature instance or NULL on error
311
*/
312
IVFKFeature *IVFKDataBlock::GetLastFeature()
313
0
{
314
0
    if (m_nFeatureCount < 0)
315
0
    {
316
0
        m_poReader->ReadDataRecords(this);
317
0
    }
318
319
0
    if (m_bGeometryPerBlock && !m_bGeometry)
320
0
    {
321
0
        LoadGeometry();
322
0
    }
323
324
0
    if (m_nFeatureCount < 1)
325
0
        return nullptr;
326
327
0
    return m_papoFeature[m_nFeatureCount - 1];
328
0
}
329
330
/*!
331
  \brief Get property index by name
332
333
  \param pszName property name
334
335
  \return property index or -1 on error (property name not found)
336
*/
337
int IVFKDataBlock::GetPropertyIndex(const char *pszName) const
338
6.62k
{
339
59.8k
    for (int i = 0; i < m_nPropertyCount; i++)
340
59.7k
        if (EQUAL(pszName, m_papoProperty[i]->GetName()))
341
6.58k
            return i;
342
343
42
    return -1;
344
6.62k
}
345
346
/*!
347
  \brief Set geometry type (point, linestring, polygon)
348
349
  \param bSuppressGeometry True for forcing wkbNone type
350
351
  \return geometry type
352
*/
353
OGRwkbGeometryType IVFKDataBlock::SetGeometryType(bool bSuppressGeometry)
354
8.48k
{
355
8.48k
    m_nGeometryType = wkbNone; /* pure attribute records */
356
8.48k
    if (bSuppressGeometry)
357
0
    {
358
0
        m_bGeometry = true; /* pretend that geometry is already loaded */
359
360
0
        return m_nGeometryType;
361
0
    }
362
363
8.48k
    if (EQUAL(m_pszName, "SOBR") || EQUAL(m_pszName, "OBBP") ||
364
8.24k
        EQUAL(m_pszName, "SPOL") || EQUAL(m_pszName, "OB") ||
365
8.17k
        EQUAL(m_pszName, "OP") || EQUAL(m_pszName, "OBPEJ"))
366
366
        m_nGeometryType = wkbPoint;
367
368
8.12k
    else if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG") ||
369
7.87k
             EQUAL(m_pszName, "HP") || EQUAL(m_pszName, "DPM") ||
370
7.50k
             EQUAL(m_pszName, "ZVB"))
371
639
        m_nGeometryType = wkbLineString;
372
373
7.48k
    else if (EQUAL(m_pszName, "PAR") || EQUAL(m_pszName, "BUD"))
374
105
        m_nGeometryType = wkbPolygon;
375
376
8.48k
    return m_nGeometryType;
377
8.48k
}
378
379
/*!
380
  \brief Get geometry type
381
382
  \return geometry type
383
*/
384
OGRwkbGeometryType IVFKDataBlock::GetGeometryType() const
385
280k
{
386
280k
    return m_nGeometryType;
387
280k
}
388
389
/*!
390
  \brief Get feature by index
391
392
  \param iIndex feature index
393
394
  \return pointer to feature definition or NULL on failure
395
*/
396
IVFKFeature *IVFKDataBlock::GetFeatureByIndex(int iIndex) const
397
19.3k
{
398
19.3k
    if (iIndex < 0 || iIndex >= m_nFeatureCount)
399
219
        return nullptr;
400
401
19.1k
    return m_papoFeature[iIndex];
402
19.3k
}
403
404
/*!
405
  \brief Get feature by FID
406
407
  Modifies next feature id.
408
409
  \param nFID feature id
410
411
  \return pointer to feature definition or NULL on failure (not found)
412
*/
413
IVFKFeature *IVFKDataBlock::GetFeature(GIntBig nFID)
414
0
{
415
0
    if (m_nFeatureCount < 0)
416
0
    {
417
0
        m_poReader->ReadDataRecords(this);
418
0
    }
419
420
0
    if (nFID < 1 || nFID > m_nFeatureCount)
421
0
        return nullptr;
422
423
0
    if (m_bGeometryPerBlock && !m_bGeometry)
424
0
    {
425
0
        LoadGeometry();
426
0
    }
427
428
0
    return GetFeatureByIndex(int(nFID) - 1); /* zero-based index */
429
0
}
430
431
/*!
432
  \brief Load geometry
433
434
  Print warning when some invalid features are detected.
435
436
  \return number of invalid features or -1 on failure
437
*/
438
int IVFKDataBlock::LoadGeometry()
439
8.74k
{
440
8.74k
#if defined(__GNUC__)
441
8.74k
#pragma GCC diagnostic push
442
8.74k
#pragma GCC diagnostic ignored "-Wnull-dereference"
443
8.74k
#endif
444
8.74k
    if (m_bGeometry)
445
257
        return 0;
446
8.48k
#if defined(__GNUC__)
447
8.48k
#pragma GCC diagnostic pop
448
8.48k
#endif
449
450
8.48k
    m_bGeometry = true;
451
8.48k
    int nInvalid = 0;
452
453
#ifdef DEBUG_TIMING
454
    const clock_t start = clock();
455
#endif
456
457
8.48k
    if (m_nFeatureCount < 0)
458
0
    {
459
0
        m_poReader->ReadDataRecords(this);
460
0
    }
461
462
8.48k
    if (EQUAL(m_pszName, "SOBR") || EQUAL(m_pszName, "SPOL") ||
463
8.25k
        EQUAL(m_pszName, "OP") || EQUAL(m_pszName, "OBPEJ") ||
464
8.20k
        EQUAL(m_pszName, "OB") || EQUAL(m_pszName, "OBBP"))
465
366
    {
466
        /* -> wkbPoint */
467
366
        nInvalid = LoadGeometryPoint();
468
366
    }
469
8.12k
    else if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
470
252
    {
471
        /* -> wkbLineString */
472
252
        nInvalid = LoadGeometryLineStringSBP();
473
252
    }
474
7.87k
    else if (EQUAL(m_pszName, "HP") || EQUAL(m_pszName, "DPM") ||
475
7.50k
             EQUAL(m_pszName, "ZVB"))
476
387
    {
477
        /* -> wkbLineString */
478
387
        nInvalid = LoadGeometryLineStringHP();
479
387
    }
480
7.48k
    else if (EQUAL(m_pszName, "PAR") || EQUAL(m_pszName, "BUD"))
481
105
    {
482
        /* -> wkbPolygon */
483
105
        nInvalid = LoadGeometryPolygon();
484
105
    }
485
486
#ifdef DEBUG_TIMING
487
    const clock_t end = clock();
488
#endif
489
490
8.48k
    if (nInvalid > 0)
491
132
    {
492
132
        CPLError(CE_Warning, CPLE_AppDefined,
493
132
                 "%s: %d features with invalid or empty geometry", m_pszName,
494
132
                 nInvalid);
495
132
    }
496
497
#ifdef DEBUG_TIMING
498
    CPLDebug("OGR-VFK", "VFKDataBlock::LoadGeometry(): name=%s time=%ld sec",
499
             m_pszName, (long)((end - start) / CLOCKS_PER_SEC));
500
#endif
501
502
8.48k
    return nInvalid;
503
8.74k
}
504
505
void IVFKDataBlock::FillPointList(PointList *poList,
506
                                  const OGRLineString *poLine)
507
0
{
508
0
    poList->reserve(poLine->getNumPoints());
509
510
    /* OGRLineString -> PointList */
511
0
    for (int i = 0; i < poLine->getNumPoints(); i++)
512
0
    {
513
0
        OGRPoint pt;
514
0
        poLine->getPoint(i, &pt);
515
0
        poList->emplace_back(std::move(pt));
516
0
    }
517
0
}
518
519
/*!
520
  \brief Add linestring to a ring (private)
521
522
  \param[in,out] papoRing list of rings
523
  \param poLine pointer to linestring to be added to a ring
524
  \param bNewRing  create new ring
525
  \param bBackward allow backward direction
526
527
  \return true on success or false on failure
528
*/
529
bool IVFKDataBlock::AppendLineToRing(PointListArray *papoRing,
530
                                     const OGRLineString *poLine, bool bNewRing,
531
                                     bool bBackward)
532
0
{
533
    /* create new ring */
534
0
    if (bNewRing)
535
0
    {
536
0
        PointList *poList = new PointList();
537
0
        FillPointList(poList, poLine);
538
0
        papoRing->emplace_back(poList);
539
0
        return true;
540
0
    }
541
542
0
    if (poLine->getNumPoints() < 2)
543
0
        return false;
544
0
    OGRPoint oFirstNew;
545
0
    OGRPoint oLastNew;
546
0
    poLine->StartPoint(&oFirstNew);
547
0
    poLine->EndPoint(&oLastNew);
548
549
0
    for (PointList *ring : *papoRing)
550
0
    {
551
0
        const OGRPoint &oFirst = ring->front();
552
0
        const OGRPoint &oLast = ring->back();
553
554
0
        if (oFirstNew.getX() == oLast.getX() &&
555
0
            oFirstNew.getY() == oLast.getY())
556
0
        {
557
0
            PointList oList;
558
0
            FillPointList(&oList, poLine);
559
            /* forward, skip first point */
560
0
            ring->insert(ring->end(), oList.begin() + 1, oList.end());
561
0
            return true;
562
0
        }
563
564
0
        if (bBackward && oFirstNew.getX() == oFirst.getX() &&
565
0
            oFirstNew.getY() == oFirst.getY())
566
0
        {
567
0
            PointList oList;
568
0
            FillPointList(&oList, poLine);
569
            /* backward, skip last point */
570
0
            ring->insert(ring->begin(), oList.rbegin(), oList.rend() - 1);
571
0
            return true;
572
0
        }
573
574
0
        if (oLastNew.getX() == oLast.getX() && oLastNew.getY() == oLast.getY())
575
0
        {
576
0
            PointList oList;
577
0
            FillPointList(&oList, poLine);
578
            /* backward, skip first point */
579
0
            ring->insert(ring->end(), oList.rbegin() + 1, oList.rend());
580
0
            return true;
581
0
        }
582
583
0
        if (bBackward && oLastNew.getX() == oFirst.getX() &&
584
0
            oLastNew.getY() == oFirst.getY())
585
0
        {
586
0
            PointList oList;
587
0
            FillPointList(&oList, poLine);
588
            /* forward, skip last point */
589
0
            ring->insert(ring->begin(), oList.begin(), oList.end() - 1);
590
0
            return true;
591
0
        }
592
0
    }
593
594
0
    return false;
595
0
}
596
597
/*!
598
  \brief Set next feature
599
600
  \param poFeature pointer to current feature
601
602
  \return index of current feature or -1 on failure
603
*/
604
int IVFKDataBlock::SetNextFeature(const IVFKFeature *poFeature)
605
0
{
606
0
    for (int i = 0; i < m_nFeatureCount; i++)
607
0
    {
608
0
        if (m_papoFeature[i] == poFeature)
609
0
        {
610
0
            m_iNextFeature = i + 1;
611
0
            return i;
612
0
        }
613
0
    }
614
615
0
    return -1;
616
0
}
617
618
/*!
619
  \brief Add feature
620
621
  \param poNewFeature pointer to VFKFeature instance
622
*/
623
void IVFKDataBlock::AddFeature(IVFKFeature *poNewFeature)
624
16.3k
{
625
16.3k
    m_nFeatureCount++;
626
627
16.3k
    m_papoFeature = (IVFKFeature **)CPLRealloc(
628
16.3k
        m_papoFeature, sizeof(IVFKFeature *) * m_nFeatureCount);
629
16.3k
    m_papoFeature[m_nFeatureCount - 1] = poNewFeature;
630
16.3k
}
631
632
/*!
633
  \brief Get number of records
634
635
  \param iRec record type (valid, skipped, duplicated)
636
637
  \return number of records
638
*/
639
int IVFKDataBlock::GetRecordCount(RecordType iRec) const
640
47.1k
{
641
47.1k
    return (int)m_nRecordCount[iRec];
642
47.1k
}
643
644
/*!
645
  \brief Increment number of records
646
647
  \param iRec record type (valid, skipped, duplicated)
648
*/
649
void IVFKDataBlock::SetIncRecordCount(RecordType iRec)
650
153k
{
651
153k
    m_nRecordCount[iRec]++;
652
153k
}
653
654
/*!
655
  \brief Get first found feature based on its properties
656
657
  Note: modifies next feature.
658
659
  \param idx property index
660
  \param value property value
661
  \param poList list of features (NULL to loop all features)
662
663
  \return pointer to feature definition or NULL on failure (not found)
664
*/
665
VFKFeature *VFKDataBlock::GetFeature(int idx, GUIntBig value,
666
                                     VFKFeatureList *poList)
667
0
{
668
0
    if (poList)
669
0
    {
670
0
        for (VFKFeatureList::iterator i = poList->begin(), e = poList->end();
671
0
             i != e; ++i)
672
0
        {
673
0
            VFKFeature *poVfkFeature = *i;
674
0
            const GUIntBig iPropertyValue = strtoul(
675
0
                poVfkFeature->GetProperty(idx)->GetValueS(), nullptr, 0);
676
0
            if (iPropertyValue == value)
677
0
            {
678
0
                poList->erase(i); /* ??? */
679
0
                return poVfkFeature;
680
0
            }
681
0
        }
682
0
    }
683
0
    else
684
0
    {
685
0
        for (int i = 0; i < m_nFeatureCount; i++)
686
0
        {
687
0
            VFKFeature *poVfkFeature =
688
0
                cpl::down_cast<VFKFeature *>(GetFeatureByIndex(i));
689
0
            const GUIntBig iPropertyValue = strtoul(
690
0
                poVfkFeature->GetProperty(idx)->GetValueS(), nullptr, 0);
691
0
            if (iPropertyValue == value)
692
0
            {
693
0
                m_iNextFeature = i + 1;
694
0
                return poVfkFeature;
695
0
            }
696
0
        }
697
0
    }
698
699
0
    return nullptr;
700
0
}
701
702
/*!
703
  \brief Get features based on properties
704
705
  \param idx property index
706
  \param value property value
707
708
  \return list of features
709
*/
710
VFKFeatureList VFKDataBlock::GetFeatures(int idx, GUIntBig value)
711
0
{
712
0
    std::vector<VFKFeature *> poResult;
713
714
0
    for (int i = 0; i < m_nFeatureCount; i++)
715
0
    {
716
0
        VFKFeature *poVfkFeature =
717
0
            cpl::down_cast<VFKFeature *>(GetFeatureByIndex(i));
718
0
        const GUIntBig iPropertyValue =
719
0
            strtoul(poVfkFeature->GetProperty(idx)->GetValueS(), nullptr, 0);
720
0
        if (iPropertyValue == value)
721
0
        {
722
0
            poResult.push_back(poVfkFeature);
723
0
        }
724
0
    }
725
726
0
    return poResult;
727
0
}
728
729
/*!
730
  \brief Get features based on properties
731
732
  \param idx1 property index
733
  \param idx2 property index
734
  \param value property value
735
736
  \return list of features
737
*/
738
VFKFeatureList VFKDataBlock::GetFeatures(int idx1, int idx2, GUIntBig value)
739
0
{
740
0
    std::vector<VFKFeature *> poResult;
741
742
0
    for (int i = 0; i < m_nFeatureCount; i++)
743
0
    {
744
0
        VFKFeature *poVfkFeature =
745
0
            cpl::down_cast<VFKFeature *>(GetFeatureByIndex(i));
746
0
        const GUIntBig iPropertyValue1 =
747
0
            strtoul(poVfkFeature->GetProperty(idx1)->GetValueS(), nullptr, 0);
748
0
        if (idx2 < 0)
749
0
        {
750
0
            if (iPropertyValue1 == value)
751
0
            {
752
0
                poResult.push_back(poVfkFeature);
753
0
            }
754
0
        }
755
0
        else
756
0
        {
757
0
            const GUIntBig iPropertyValue2 = strtoul(
758
0
                poVfkFeature->GetProperty(idx2)->GetValueS(), nullptr, 0);
759
0
            if (iPropertyValue1 == value || iPropertyValue2 == value)
760
0
            {
761
0
                poResult.push_back(poVfkFeature);
762
0
            }
763
0
        }
764
0
    }
765
766
0
    return poResult;
767
0
}
768
769
/*!
770
  \brief Get feature count based on property value
771
772
  \param pszName property name
773
  \param pszValue property value
774
775
  \return number of features or -1 on error
776
*/
777
GIntBig VFKDataBlock::GetFeatureCount(const char *pszName, const char *pszValue)
778
0
{
779
0
    const int propIdx = GetPropertyIndex(pszName);
780
0
    if (propIdx < 0)
781
0
        return -1;
782
783
0
    int nfeatures = 0;
784
0
    for (int i = 0; i < ((IVFKDataBlock *)this)->GetFeatureCount(); i++)
785
0
    {
786
0
        VFKFeature *poVFKFeature = cpl::down_cast<VFKFeature *>(
787
0
            ((IVFKDataBlock *)this)->GetFeature(i));
788
0
        if (!poVFKFeature)
789
0
            return -1;
790
0
        if (EQUAL(poVFKFeature->GetProperty(propIdx)->GetValueS(), pszValue))
791
0
            nfeatures++;
792
0
    }
793
794
0
    return nfeatures;
795
0
}
796
797
/*!
798
  \brief Load geometry (point layers)
799
800
  \return number of invalid features
801
*/
802
int VFKDataBlock::LoadGeometryPoint()
803
0
{
804
0
    int nInvalid = 0;
805
0
    int i_idxY = GetPropertyIndex("SOURADNICE_Y");
806
0
    int i_idxX = GetPropertyIndex("SOURADNICE_X");
807
0
    if (i_idxY < 0 || i_idxX < 0)
808
0
    {
809
0
        CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data (%s).\n",
810
0
                 m_pszName);
811
0
        return nInvalid;
812
0
    }
813
814
0
    for (int j = 0; j < ((IVFKDataBlock *)this)->GetFeatureCount(); j++)
815
0
    {
816
0
        VFKFeature *poFeature =
817
0
            cpl::down_cast<VFKFeature *>(GetFeatureByIndex(j));
818
0
        double x = -1.0 * poFeature->GetProperty(i_idxY)->GetValueD();
819
0
        double y = -1.0 * poFeature->GetProperty(i_idxX)->GetValueD();
820
0
        OGRPoint pt(x, y);
821
0
        if (!poFeature->SetGeometry(&pt))
822
0
            nInvalid++;
823
0
    }
824
825
0
    return nInvalid;
826
0
}
827
828
/*!
829
  \brief Load geometry (linestring SBP/SBPG layer)
830
831
  \return number of invalid features
832
*/
833
int VFKDataBlock::LoadGeometryLineStringSBP()
834
0
{
835
0
    VFKDataBlock *poDataBlockPoints =
836
0
        cpl::down_cast<VFKDataBlock *>(m_poReader->GetDataBlock("SOBR"));
837
0
    if (nullptr == poDataBlockPoints)
838
0
    {
839
0
        CPLError(CE_Failure, CPLE_NotSupported, "Data block %s not found.\n",
840
0
                 m_pszName);
841
0
        return 0;
842
0
    }
843
844
0
    poDataBlockPoints->LoadGeometry();
845
0
    int idxId = poDataBlockPoints->GetPropertyIndex("ID");
846
0
    int idxBp_Id = GetPropertyIndex("BP_ID");
847
0
    int idxPCB = GetPropertyIndex("PORADOVE_CISLO_BODU");
848
0
    if (idxId < 0 || idxBp_Id < 0 || idxPCB < 0)
849
0
    {
850
0
        CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data (%s).\n",
851
0
                 m_pszName);
852
0
        return 0;
853
0
    }
854
855
0
    OGRLineString oOGRLine;
856
0
    VFKFeature *poLine = nullptr;
857
0
    int nInvalid = 0;
858
859
0
    for (int j = 0; j < ((IVFKDataBlock *)this)->GetFeatureCount(); j++)
860
0
    {
861
0
        VFKFeature *poFeature =
862
0
            cpl::down_cast<VFKFeature *>(GetFeatureByIndex(j));
863
0
        CPLAssert(nullptr != poFeature);
864
865
0
        poFeature->SetGeometry(nullptr);
866
0
        GUIntBig id =
867
0
            strtoul(poFeature->GetProperty(idxBp_Id)->GetValueS(), nullptr, 0);
868
0
        GUIntBig ipcb =
869
0
            strtoul(poFeature->GetProperty(idxPCB)->GetValueS(), nullptr, 0);
870
0
        if (ipcb == 1)
871
0
        {
872
0
            if (!oOGRLine.IsEmpty())
873
0
            {
874
0
                oOGRLine.setCoordinateDimension(2); /* force 2D */
875
0
                if (poLine != nullptr && !poLine->SetGeometry(&oOGRLine))
876
0
                    nInvalid++;
877
0
                oOGRLine.empty(); /* restore line */
878
0
            }
879
0
            poLine = poFeature;
880
0
        }
881
0
        else
882
0
        {
883
0
            poFeature->SetGeometryType(wkbUnknown);
884
0
        }
885
0
        VFKFeature *poPoint = poDataBlockPoints->GetFeature(idxId, id);
886
0
        if (!poPoint)
887
0
            continue;
888
0
        const OGRPoint *pt = poPoint->GetGeometry()->toPoint();
889
0
        oOGRLine.addPoint(pt);
890
0
    }
891
    /* add last line */
892
0
    oOGRLine.setCoordinateDimension(2); /* force 2D */
893
0
    if (poLine)
894
0
    {
895
0
        if (!poLine->SetGeometry(&oOGRLine))
896
0
            nInvalid++;
897
0
    }
898
0
    poDataBlockPoints->ResetReading();
899
900
0
    return nInvalid;
901
0
}
902
903
/*!
904
  \brief Load geometry (linestring HP/DPM/ZVB layer)
905
906
  \return number of invalid features
907
*/
908
int VFKDataBlock::LoadGeometryLineStringHP()
909
0
{
910
0
    int nInvalid = 0;
911
912
0
    VFKDataBlock *poDataBlockLines =
913
0
        cpl::down_cast<VFKDataBlock *>(m_poReader->GetDataBlock("SBP"));
914
0
    if (nullptr == poDataBlockLines)
915
0
    {
916
0
        CPLError(CE_Failure, CPLE_NotSupported, "Data block %s not found.\n",
917
0
                 m_pszName);
918
0
        return nInvalid;
919
0
    }
920
921
0
    poDataBlockLines->LoadGeometry();
922
0
    const int idxId = GetPropertyIndex("ID");
923
0
    CPLString osColumn;
924
0
    osColumn.Printf("%s_ID", m_pszName);
925
0
    const int idxMy_Id = poDataBlockLines->GetPropertyIndex(osColumn);
926
0
    const int idxPCB =
927
0
        poDataBlockLines->GetPropertyIndex("PORADOVE_CISLO_BODU");
928
0
    if (idxId < 0 || idxMy_Id < 0 || idxPCB < 0)
929
0
    {
930
0
        CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data (%s).\n",
931
0
                 m_pszName);
932
0
        return nInvalid;
933
0
    }
934
935
    // Reduce to first segment.
936
0
    VFKFeatureList poLineList = poDataBlockLines->GetFeatures(idxPCB, 1);
937
0
    for (int i = 0; i < ((IVFKDataBlock *)this)->GetFeatureCount(); i++)
938
0
    {
939
0
        VFKFeature *poFeature =
940
0
            cpl::down_cast<VFKFeature *>(GetFeatureByIndex(i));
941
0
        CPLAssert(nullptr != poFeature);
942
0
        GUIntBig id =
943
0
            strtoul(poFeature->GetProperty(idxId)->GetValueS(), nullptr, 0);
944
0
        VFKFeature *poLine =
945
0
            poDataBlockLines->GetFeature(idxMy_Id, id, &poLineList);
946
0
        if (!poLine || !poLine->GetGeometry())
947
0
            continue;
948
0
        if (!poFeature->SetGeometry(poLine->GetGeometry()))
949
0
            nInvalid++;
950
0
    }
951
0
    poDataBlockLines->ResetReading();
952
953
0
    return nInvalid;
954
0
}
955
956
/*!
957
  \brief Load geometry (polygon BUD/PAR layers)
958
959
  \return number of invalid features
960
*/
961
int VFKDataBlock::LoadGeometryPolygon()
962
0
{
963
0
    VFKDataBlock *poDataBlockLines1 = nullptr;
964
0
    VFKDataBlock *poDataBlockLines2 = nullptr;
965
966
0
    bool bIsPar = false;
967
0
    if (EQUAL(m_pszName, "PAR"))
968
0
    {
969
0
        poDataBlockLines1 =
970
0
            cpl::down_cast<VFKDataBlock *>(m_poReader->GetDataBlock("HP"));
971
0
        poDataBlockLines2 = poDataBlockLines1;
972
0
        bIsPar = true;
973
0
    }
974
0
    else
975
0
    {
976
0
        poDataBlockLines1 =
977
0
            cpl::down_cast<VFKDataBlock *>(m_poReader->GetDataBlock("OB"));
978
0
        poDataBlockLines2 =
979
0
            cpl::down_cast<VFKDataBlock *>(m_poReader->GetDataBlock("SBP"));
980
0
        bIsPar = false;
981
0
    }
982
983
0
    int nInvalid = 0;
984
0
    if (nullptr == poDataBlockLines1 || nullptr == poDataBlockLines2)
985
0
    {
986
0
        CPLError(CE_Failure, CPLE_NotSupported, "Data block %s not found.\n",
987
0
                 m_pszName);
988
0
        return nInvalid;
989
0
    }
990
991
0
    poDataBlockLines1->LoadGeometry();
992
0
    poDataBlockLines2->LoadGeometry();
993
0
    int idxId = GetPropertyIndex("ID");
994
0
    if (idxId < 0)
995
0
    {
996
0
        CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data (%s).\n",
997
0
                 m_pszName);
998
0
        return nInvalid;
999
0
    }
1000
1001
0
    int idxBud = 0;
1002
0
    int idxOb = 0;
1003
0
    int idxIdOb = 0;
1004
0
    int idxPar1 = 0;
1005
0
    int idxPar2 = 0;
1006
0
    if (bIsPar)
1007
0
    {
1008
0
        idxPar1 = poDataBlockLines1->GetPropertyIndex("PAR_ID_1");
1009
0
        idxPar2 = poDataBlockLines1->GetPropertyIndex("PAR_ID_2");
1010
0
        if (idxPar1 < 0 || idxPar2 < 0)
1011
0
        {
1012
0
            CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data (%s).\n",
1013
0
                     m_pszName);
1014
0
            return nInvalid;
1015
0
        }
1016
0
    }
1017
0
    else
1018
0
    { /* BUD */
1019
0
        idxIdOb = poDataBlockLines1->GetPropertyIndex("ID");
1020
0
        idxBud = poDataBlockLines1->GetPropertyIndex("BUD_ID");
1021
0
        idxOb = poDataBlockLines2->GetPropertyIndex("OB_ID");
1022
0
        if (idxIdOb < 0 || idxBud < 0 || idxOb < 0)
1023
0
        {
1024
0
            CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data (%s).\n",
1025
0
                     m_pszName);
1026
0
            return nInvalid;
1027
0
        }
1028
0
    }
1029
1030
0
    VFKFeatureList poLineList;
1031
0
    PointListArray poRingList; /* first is to be considered as exterior */
1032
0
    OGRLinearRing ogrRing;
1033
0
    OGRPolygon ogrPolygon;
1034
1035
0
    for (int i = 0; i < ((IVFKDataBlock *)this)->GetFeatureCount(); i++)
1036
0
    {
1037
0
        VFKFeature *poFeature =
1038
0
            cpl::down_cast<VFKFeature *>(GetFeatureByIndex(i));
1039
0
        CPLAssert(nullptr != poFeature);
1040
0
        const GUIntBig id =
1041
0
            strtoul(poFeature->GetProperty(idxId)->GetValueS(), nullptr, 0);
1042
0
        if (bIsPar)
1043
0
        {
1044
0
            poLineList = poDataBlockLines1->GetFeatures(idxPar1, idxPar2, id);
1045
0
        }
1046
0
        else
1047
0
        {
1048
0
            VFKFeature *poLineOb, *poLineSbp;
1049
0
            std::vector<VFKFeature *> poLineListOb;
1050
0
            poLineListOb = poDataBlockLines1->GetFeatures(idxBud, id);
1051
0
            for (std::vector<VFKFeature *>::const_iterator
1052
0
                     iOb = poLineListOb.begin(),
1053
0
                     eOb = poLineListOb.end();
1054
0
                 iOb != eOb; ++iOb)
1055
0
            {
1056
0
                poLineOb = (*iOb);
1057
0
                GUIntBig idOb = strtoul(
1058
0
                    poLineOb->GetProperty(idxIdOb)->GetValueS(), nullptr, 0);
1059
0
                poLineSbp = poDataBlockLines2->GetFeature(idxOb, idOb);
1060
0
                if (poLineSbp)
1061
0
                    poLineList.push_back(poLineSbp);
1062
0
            }
1063
0
        }
1064
0
        if (poLineList.size() < 1)
1065
0
            continue;
1066
1067
        /* clear */
1068
0
        ogrPolygon.empty();
1069
0
        poRingList.clear();
1070
1071
        /* collect rings (points) */
1072
0
        bool bFound = false;
1073
0
        int nCount = 0;
1074
0
        int nCountMax = static_cast<int>(poLineList.size()) * 2;
1075
0
        while (!poLineList.empty() && nCount < nCountMax)
1076
0
        {
1077
0
            bool bNewRing = !bFound;
1078
0
            bFound = false;
1079
0
            for (VFKFeatureList::iterator iHp = poLineList.begin(),
1080
0
                                          eHp = poLineList.end();
1081
0
                 iHp != eHp; ++iHp)
1082
0
            {
1083
0
                auto pGeom = (*iHp)->GetGeometry();
1084
0
                if (pGeom && AppendLineToRing(&poRingList,
1085
0
                                              pGeom->toLineString(), bNewRing))
1086
0
                {
1087
0
                    bFound = true;
1088
0
                    poLineList.erase(iHp);
1089
0
                    break;
1090
0
                }
1091
0
            }
1092
0
            nCount++;
1093
0
        }
1094
        /* create rings */
1095
0
        for (PointListArray::const_iterator iRing = poRingList.begin(),
1096
0
                                            eRing = poRingList.end();
1097
0
             iRing != eRing; ++iRing)
1098
0
        {
1099
0
            PointList *poList = *iRing;
1100
0
            ogrRing.empty();
1101
0
            for (PointList::iterator iPoint = poList->begin(),
1102
0
                                     ePoint = poList->end();
1103
0
                 iPoint != ePoint; ++iPoint)
1104
0
            {
1105
0
                ogrRing.addPoint(&(*iPoint));
1106
0
            }
1107
0
            ogrPolygon.addRing(&ogrRing);
1108
0
        }
1109
        /* set polygon */
1110
0
        ogrPolygon.setCoordinateDimension(2); /* force 2D */
1111
0
        if (!poFeature->SetGeometry(&ogrPolygon))
1112
0
            nInvalid++;
1113
0
    }
1114
1115
    /* free ring list */
1116
0
    for (PointListArray::iterator iRing = poRingList.begin(),
1117
0
                                  eRing = poRingList.end();
1118
0
         iRing != eRing; ++iRing)
1119
0
    {
1120
0
        delete (*iRing);
1121
0
        *iRing = nullptr;
1122
0
    }
1123
0
    poDataBlockLines1->ResetReading();
1124
0
    poDataBlockLines2->ResetReading();
1125
1126
0
    return nInvalid;
1127
0
}