Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/vfk/vfkdatablocksqlite.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  VFK Reader - Data block definition (SQLite)
4
 * Purpose:  Implements VFKDataBlockSQLite
5
 * Author:   Martin Landa, landa.martin gmail.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2012-2014, Martin Landa <landa.martin gmail.com>
9
 * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include <algorithm>
15
#include <limits>
16
#include <map>
17
#include <utility>
18
19
#include "vfkreader.h"
20
#include "vfkreaderp.h"
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
25
/*!
26
  \brief VFKDataBlockSQLite constructor
27
*/
28
VFKDataBlockSQLite::VFKDataBlockSQLite(const char *pszName,
29
                                       const IVFKReader *poReader)
30
10.3k
    : IVFKDataBlock(pszName, poReader), m_hStmt(nullptr)
31
10.3k
{
32
10.3k
}
33
34
/*!
35
  \brief Load geometry (point layers)
36
37
  \return number of invalid features
38
*/
39
int VFKDataBlockSQLite::LoadGeometryPoint()
40
498
{
41
498
    if (LoadGeometryFromDB()) /* try to load geometry from DB */
42
97
        return 0;
43
44
401
    const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
45
401
                              EQUAL(m_pszName, "OP") ||
46
401
                              EQUAL(m_pszName, "OBBP");
47
48
401
    CPLString osSQL;
49
401
    osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s",
50
401
                 FID_COLUMN, m_pszName);
51
52
401
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
53
401
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
54
55
401
    if (poReader->IsSpatial())
56
401
        poReader->ExecuteSQL("BEGIN");
57
58
401
    int nGeometries = 0;
59
401
    int nInvalid = 0;
60
11.3k
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
61
10.9k
    {
62
        /* read values */
63
10.9k
        const double x =
64
10.9k
            -1.0 * sqlite3_column_double(
65
10.9k
                       hStmt, 0); /* S-JTSK coordinate system expected */
66
10.9k
        const double y = -1.0 * sqlite3_column_double(hStmt, 1);
67
10.9k
        const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
68
10.9k
        const int rowId = sqlite3_column_int(hStmt, 3);
69
70
10.9k
        VFKFeatureSQLite *poFeature =
71
10.9k
            dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
72
10.9k
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
73
26
        {
74
26
            continue;
75
26
        }
76
77
        /* create geometry */
78
10.9k
        OGRPoint pt(x, y);
79
10.9k
        if (!poFeature->SetGeometry(&pt))
80
1.63k
        {
81
1.63k
            nInvalid++;
82
1.63k
            continue;
83
1.63k
        }
84
85
        /* store also geometry in DB */
86
9.29k
        if (poReader->IsSpatial() &&
87
9.29k
            SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE)
88
9.29k
            nGeometries++;
89
9.29k
    }
90
91
    /* update number of geometries in VFK_DB_TABLE table */
92
401
    UpdateVfkBlocks(nGeometries);
93
94
401
    if (poReader->IsSpatial())
95
401
        poReader->ExecuteSQL("COMMIT");
96
97
401
    return bSkipInvalid ? 0 : nInvalid;
98
498
}
99
100
/*!
101
  \brief Set geometry for linestrings
102
103
  \param poLine VFK feature
104
  \param oOGRLine line geometry
105
  \param[in,out] bValid true when feature's geometry is valid
106
  \param ftype geometry VFK type
107
  \param[in,out] rowIdFeat list of row ids which forms linestring
108
  \param[in,out] nGeometries number of features with valid geometry
109
*/
110
bool VFKDataBlockSQLite::SetGeometryLineString(VFKFeatureSQLite *poLine,
111
                                               OGRLineString *oOGRLine,
112
                                               bool &bValid, const char *ftype,
113
                                               std::vector<int> &rowIdFeat,
114
                                               int &nGeometries)
115
5.24k
{
116
5.24k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
117
118
5.24k
    oOGRLine->setCoordinateDimension(2); /* force 2D */
119
120
    /* check also VFK validity */
121
5.24k
    if (bValid)
122
2.91k
    {
123
        /* Feature types
124
125
           - '3'    - line       (2 points)
126
           - '4'    - linestring (at least 2 points)
127
           - '11'   - curve      (at least 2 points)
128
           - '15'   - circle     (3 points)
129
           - '15 r' - circle     (center point & radius)
130
           - '16'   - arc        (3 points)
131
        */
132
133
2.91k
        const int npoints = oOGRLine->getNumPoints();
134
2.91k
        if (EQUAL(ftype, "3") && npoints > 2)
135
1
        {
136
            /* be less pedantic, just inform user about data
137
             * inconsistency
138
139
               bValid = false;
140
            */
141
1
            CPLDebug("OGR-VFK",
142
1
                     "Line (fid=" CPL_FRMT_GIB
143
1
                     ") defined by more than two vertices",
144
1
                     poLine->GetFID());
145
1
        }
146
2.90k
        else if (EQUAL(ftype, "11") && npoints < 2)
147
0
        {
148
0
            bValid = false;
149
0
            CPLError(CE_Warning, CPLE_AppDefined,
150
0
                     "Curve (fid=" CPL_FRMT_GIB
151
0
                     ") defined by less than two vertices",
152
0
                     poLine->GetFID());
153
0
        }
154
2.90k
        else if (EQUAL(ftype, "15") && npoints != 3)
155
0
        {
156
0
            bValid = false;
157
0
            CPLError(CE_Warning, CPLE_AppDefined,
158
0
                     "Circle (fid=" CPL_FRMT_GIB
159
0
                     ") defined by invalid number of vertices (%d)",
160
0
                     poLine->GetFID(), oOGRLine->getNumPoints());
161
0
        }
162
2.90k
        else if (strlen(ftype) > 2 && STARTS_WITH_CI(ftype, "15") &&
163
2.90k
                 npoints != 1)
164
0
        {
165
0
            bValid = false;
166
0
            CPLError(CE_Warning, CPLE_AppDefined,
167
0
                     "Circle (fid=" CPL_FRMT_GIB
168
0
                     ") defined by invalid number of vertices (%d)",
169
0
                     poLine->GetFID(), oOGRLine->getNumPoints());
170
0
        }
171
2.90k
        else if (EQUAL(ftype, "16") && npoints != 3)
172
130
        {
173
130
            bValid = false;
174
130
            CPLError(CE_Warning, CPLE_AppDefined,
175
130
                     "Arc (fid=" CPL_FRMT_GIB
176
130
                     ") defined by invalid number of vertices (%d)",
177
130
                     poLine->GetFID(), oOGRLine->getNumPoints());
178
130
        }
179
2.91k
    }
180
181
    /* set geometry (NULL for invalid features) */
182
5.24k
    if (bValid)
183
2.78k
    {
184
2.78k
        if (!poLine->SetGeometry(oOGRLine, ftype))
185
2.63k
        {
186
2.63k
            bValid = false;
187
2.63k
        }
188
2.78k
    }
189
2.46k
    else
190
2.46k
    {
191
2.46k
        poLine->SetGeometry(nullptr);
192
2.46k
    }
193
194
    /* update fid column */
195
5.24k
    UpdateFID(poLine->GetFID(), rowIdFeat);
196
197
    /* store also geometry in DB */
198
5.24k
    CPLAssert(!rowIdFeat.empty());
199
5.24k
    if (bValid && poReader->IsSpatial() &&
200
5.24k
        SaveGeometryToDB(poLine->GetGeometry(), rowIdFeat[0]) != OGRERR_FAILURE)
201
149
    {
202
149
        nGeometries++;
203
149
    }
204
205
5.24k
    rowIdFeat.clear();
206
5.24k
    oOGRLine->empty(); /* restore line */
207
208
5.24k
    return bValid;
209
5.24k
}
210
211
/*!
212
  \brief Load geometry (linestring SBP layer)
213
214
  \return number of invalid features
215
*/
216
int VFKDataBlockSQLite::LoadGeometryLineStringSBP()
217
342
{
218
342
    int nInvalid = 0;
219
220
342
    VFKDataBlockSQLite *poDataBlockPoints =
221
342
        cpl::down_cast<VFKDataBlockSQLite *>(m_poReader->GetDataBlock("SOBR"));
222
342
    if (nullptr == poDataBlockPoints)
223
125
    {
224
125
        CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.\n",
225
125
                 m_pszName);
226
125
        return nInvalid;
227
125
    }
228
229
217
    int nGeometries = 0;
230
217
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
231
232
217
    poDataBlockPoints->LoadGeometry();
233
234
217
    if (LoadGeometryFromDB()) /* try to load geometry from DB */
235
34
        return 0;
236
237
183
    CPLString osSQL;
238
183
    osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN);
239
183
    poReader->ExecuteSQL(osSQL.c_str());
240
183
    bool bValid = true;
241
183
    int iIdx = 0;
242
243
183
    VFKFeatureSQLite *poLine = nullptr;
244
245
549
    for (int i = 0; i < 2; i++)
246
366
    {
247
        /* first collect linestrings related to HP, OB, DPM and ZVB
248
           then collect rest of linestrings */
249
366
        if (i == 0)
250
183
            osSQL.Printf(
251
183
                "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
252
183
                "FROM '%s' WHERE "
253
183
                "HP_ID IS NOT NULL OR OB_ID IS NOT NULL OR DPM_ID IS NOT NULL "
254
183
                "OR ZVB_ID IS NOT NULL "
255
183
                "ORDER BY HP_ID,OB_ID,DPM_ID,ZVB_ID,PORADOVE_CISLO_BODU",
256
183
                m_pszName);
257
183
        else
258
183
            osSQL.Printf(
259
183
                "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
260
183
                "FROM '%s' WHERE "
261
183
                "OB_ID IS NULL AND HP_ID IS NULL AND DPM_ID IS NULL AND ZVB_ID "
262
183
                "IS NULL "
263
183
                "ORDER BY ID,PORADOVE_CISLO_BODU",
264
183
                m_pszName);
265
266
366
        sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
267
268
366
        if (poReader->IsSpatial())
269
366
            poReader->ExecuteSQL("BEGIN");
270
271
366
        std::vector<int> rowIdFeat;
272
366
        CPLString osFType;
273
366
        OGRLineString oOGRLine;
274
275
11.0k
        while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
276
10.7k
        {
277
            // read values
278
10.7k
            const GUIntBig id = sqlite3_column_int64(hStmt, 0);
279
10.7k
            const GUIntBig ipcb = sqlite3_column_int64(hStmt, 1);
280
10.7k
            const char *pszFType =
281
10.7k
                reinterpret_cast<const char *>(sqlite3_column_text(hStmt, 2));
282
10.7k
            int rowId = sqlite3_column_int(hStmt, 3);
283
284
10.7k
            if (ipcb == 1)
285
5.25k
            {
286
5.25k
                VFKFeatureSQLite *poFeature =
287
5.25k
                    cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iIdx));
288
5.25k
                if (poFeature == nullptr)
289
11
                {
290
11
                    CPLError(CE_Failure, CPLE_AppDefined,
291
11
                             "Cannot retrieve feature %d", iIdx);
292
11
                    sqlite3_finalize(hStmt);
293
11
                    break;
294
11
                }
295
5.24k
                poFeature->SetRowId(rowId);
296
297
                /* set geometry & reset */
298
5.24k
                if (poLine && !SetGeometryLineString(poLine, &oOGRLine, bValid,
299
5.15k
                                                     osFType.c_str(), rowIdFeat,
300
5.15k
                                                     nGeometries))
301
5.02k
                {
302
5.02k
                    nInvalid++;
303
5.02k
                }
304
305
5.24k
                bValid = true;
306
5.24k
                poLine = poFeature;
307
5.24k
                osFType = pszFType ? pszFType : "";
308
5.24k
                iIdx++;
309
5.24k
            }
310
311
10.7k
            VFKFeatureSQLite *poPoint = cpl::down_cast<VFKFeatureSQLite *>(
312
10.7k
                poDataBlockPoints->GetFeature("ID", id));
313
10.7k
            if (poPoint)
314
6.24k
            {
315
6.24k
                const OGRGeometry *pt = poPoint->GetGeometry();
316
6.24k
                if (pt)
317
5.53k
                {
318
5.53k
                    oOGRLine.addPoint(pt->toPoint());
319
5.53k
                }
320
709
                else
321
709
                {
322
709
                    CPLDebug("OGR-VFK",
323
709
                             "Geometry (point ID = " CPL_FRMT_GUIB
324
709
                             ") not valid",
325
709
                             id);
326
709
                    bValid = false;
327
709
                }
328
6.24k
            }
329
4.47k
            else
330
4.47k
            {
331
4.47k
                CPLDebug("OGR-VFK",
332
4.47k
                         "Point ID = " CPL_FRMT_GUIB " not found (rowid = %d)",
333
4.47k
                         id, rowId);
334
4.47k
                bValid = false;
335
4.47k
            }
336
337
            /* add vertex to the linestring */
338
10.7k
            rowIdFeat.push_back(rowId);
339
10.7k
        }
340
341
        /* add last line */
342
366
        if (poLine &&
343
366
            !SetGeometryLineString(poLine, &oOGRLine, bValid, osFType.c_str(),
344
92
                                   rowIdFeat, nGeometries))
345
70
        {
346
70
            nInvalid++;
347
70
        }
348
366
        poLine = nullptr;
349
350
366
        if (poReader->IsSpatial())
351
366
            poReader->ExecuteSQL("COMMIT");
352
366
    }
353
354
    /* update number of geometries in VFK_DB_TABLE table */
355
183
    UpdateVfkBlocks(nGeometries);
356
357
183
    return nInvalid;
358
217
}
359
360
/*!
361
  \brief Load geometry (linestring HP/DPM/ZVB layer)
362
363
  \return number of invalid features
364
*/
365
int VFKDataBlockSQLite::LoadGeometryLineStringHP()
366
294
{
367
294
    int nInvalid = 0;
368
294
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
369
370
294
    VFKDataBlockSQLite *poDataBlockLines =
371
294
        cpl::down_cast<VFKDataBlockSQLite *>(m_poReader->GetDataBlock("SBP"));
372
294
    if (nullptr == poDataBlockLines)
373
188
    {
374
188
        CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.",
375
188
                 m_pszName);
376
188
        return nInvalid;
377
188
    }
378
379
106
    poDataBlockLines->LoadGeometry();
380
381
106
    if (LoadGeometryFromDB()) /* try to load geometry from DB */
382
8
        return 0;
383
384
98
    CPLString osColumn;
385
98
    osColumn.Printf("%s_ID", m_pszName);
386
98
    const char *vrColumn[2] = {osColumn.c_str(), "PORADOVE_CISLO_BODU"};
387
388
98
    GUIntBig vrValue[2] = {0, 1};  // Reduce to first segment.
389
390
98
    CPLString osSQL;
391
98
    osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
392
    /* TODO: handle points in DPM */
393
98
    if (EQUAL(m_pszName, "DPM"))
394
13
        osSQL += " WHERE SOURADNICE_X IS NULL";
395
98
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
396
397
98
    if (poReader->IsSpatial())
398
98
        poReader->ExecuteSQL("BEGIN");
399
400
98
    int nGeometries = 0;
401
402
2.23k
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
403
2.14k
    {
404
        /* read values */
405
2.14k
        vrValue[0] = sqlite3_column_int64(hStmt, 0);
406
2.14k
        const GIntBig iFID = sqlite3_column_int64(hStmt, 1);
407
2.14k
        const int rowId = sqlite3_column_int(hStmt, 2);
408
409
2.14k
        VFKFeatureSQLite *poFeature =
410
2.14k
            cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
411
2.14k
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
412
11
        {
413
11
            continue;
414
11
        }
415
416
2.13k
        VFKFeatureSQLite *poLine =
417
2.13k
            poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE);
418
419
2.13k
        const OGRGeometry *poOgrGeometry =
420
2.13k
            poLine ? poLine->GetGeometry() : nullptr;
421
2.13k
        if (!poOgrGeometry || !poFeature->SetGeometry(poOgrGeometry))
422
1.74k
        {
423
1.74k
            CPLDebug("OGR-VFK",
424
1.74k
                     "VFKDataBlockSQLite::LoadGeometryLineStringHP(): name=%s "
425
1.74k
                     "fid=" CPL_FRMT_GIB " "
426
1.74k
                     "id=" CPL_FRMT_GUIB " -> %s geometry",
427
1.74k
                     m_pszName, iFID, vrValue[0],
428
1.74k
                     poOgrGeometry ? "invalid" : "empty");
429
1.74k
            nInvalid++;
430
1.74k
            continue;
431
1.74k
        }
432
433
        /* store also geometry in DB */
434
382
        if (poReader->IsSpatial() &&
435
382
            SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE)
436
382
            nGeometries++;
437
382
    }
438
439
    /* update number of geometries in VFK_DB_TABLE table */
440
98
    UpdateVfkBlocks(nGeometries);
441
442
98
    if (poReader->IsSpatial())
443
98
        poReader->ExecuteSQL("COMMIT");
444
445
98
    return nInvalid;
446
106
}
447
448
/*!
449
  \brief Load geometry (polygon BUD/PAR layers)
450
451
  \return number of invalid features
452
*/
453
int VFKDataBlockSQLite::LoadGeometryPolygon()
454
98
{
455
98
#ifndef HAVE_GEOS
456
457
98
    CPLError(CE_Warning, CPLE_NotSupported,
458
98
             "GEOS support not enabled. Unable to build geometry for %s.",
459
98
             m_pszName);
460
98
    return -1;
461
462
#else
463
464
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
465
466
    VFKDataBlockSQLite *poDataBlockLines1 = nullptr;
467
    VFKDataBlockSQLite *poDataBlockLines2 = nullptr;
468
    bool bIsPar = false;
469
    if (EQUAL(m_pszName, "PAR"))
470
    {
471
        poDataBlockLines1 = cpl::down_cast<VFKDataBlockSQLite *>(
472
            m_poReader->GetDataBlock("HP"));
473
        poDataBlockLines2 = poDataBlockLines1;
474
        bIsPar = true;
475
    }
476
    else
477
    {
478
        poDataBlockLines1 = cpl::down_cast<VFKDataBlockSQLite *>(
479
            m_poReader->GetDataBlock("OB"));
480
        poDataBlockLines2 = cpl::down_cast<VFKDataBlockSQLite *>(
481
            m_poReader->GetDataBlock("SBP"));
482
        bIsPar = false;
483
    }
484
    if (nullptr == poDataBlockLines1)
485
    {
486
        CPLError(CE_Warning, CPLE_FileIO,
487
                 "Data block %s not found. Unable to build geometry for %s.",
488
                 bIsPar ? "HP" : "OB", m_pszName);
489
        return -1;
490
    }
491
    if (nullptr == poDataBlockLines2)
492
    {
493
        CPLError(CE_Warning, CPLE_FileIO,
494
                 "Data block %s not found. Unable to build geometry for %s.",
495
                 "SBP", m_pszName);
496
        return -1;
497
    }
498
499
    poDataBlockLines1->LoadGeometry();
500
    poDataBlockLines2->LoadGeometry();
501
502
    if (LoadGeometryFromDB())  // Try to load geometry from DB.
503
        return 0;
504
505
    const char *vrColumn[2] = {nullptr, nullptr};
506
    GUIntBig vrValue[2] = {0, 0};
507
    if (bIsPar)
508
    {
509
        vrColumn[0] = "PAR_ID_1";
510
        vrColumn[1] = "PAR_ID_2";
511
    }
512
    else
513
    {
514
        vrColumn[0] = "OB_ID";
515
        vrColumn[1] = "PORADOVE_CISLO_BODU";
516
        vrValue[1] = 1;
517
    }
518
519
    CPLString osSQL;
520
    osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
521
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
522
523
    if (poReader->IsSpatial())
524
        poReader->ExecuteSQL("BEGIN");
525
526
    int nInvalidNoLines = 0;
527
    int nInvalidNoRings = 0;
528
    int nGeometries = 0;
529
530
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
531
    {
532
        /* read values */
533
        const GUIntBig id = sqlite3_column_int64(hStmt, 0);
534
        const long iFID = static_cast<long>(sqlite3_column_int64(hStmt, 1));
535
        const int rowId = sqlite3_column_int(hStmt, 2);
536
537
        VFKFeatureSQLite *poFeature =
538
            cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
539
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
540
        {
541
            continue;
542
        }
543
544
        /* collect boundary lines */
545
        VFKFeatureSQLiteList poLineList;
546
        if (bIsPar)
547
        {
548
            vrValue[0] = vrValue[1] = id;
549
            poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2);
550
        }
551
        else
552
        {
553
            osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB,
554
                         poDataBlockLines1->GetName(), id);
555
            if (poReader->IsSpatial())
556
            {
557
                CPLString osColumn;
558
559
                osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN);
560
                osSQL += osColumn;
561
            }
562
            sqlite3_stmt *hStmtOb = poReader->PrepareStatement(osSQL.c_str());
563
564
            while (poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE)
565
            {
566
                const GUIntBig idOb = sqlite3_column_int64(hStmtOb, 0);
567
                vrValue[0] = idOb;
568
                VFKFeatureSQLite *poLineSbp =
569
                    poDataBlockLines2->GetFeature(vrColumn, vrValue, 2);
570
                if (poLineSbp)
571
                    poLineList.push_back(poLineSbp);
572
            }
573
        }
574
        if (poLineList.empty())
575
        {
576
            CPLDebug("OGR-VFK", "%s: no lines to polygonize (fid = %ld)",
577
                     m_pszName, iFID);
578
            nInvalidNoLines++;
579
            continue;
580
        }
581
582
        OGRMultiLineString oMultiLine;
583
        for (VFKFeatureSQLite *poLineFeature : poLineList)
584
        {
585
            const OGRGeometry *poLineGeom = poLineFeature->GetGeometry();
586
            if (poLineGeom)
587
            {
588
                oMultiLine.addGeometry(poLineGeom);
589
            }
590
        }
591
592
        /* polygonize using GEOSBuildArea() */
593
        auto poPolygonGeom =
594
            std::unique_ptr<OGRGeometry>(oMultiLine.BuildArea());
595
        /* only Polygons are allowed in VFK, in particular, no MultiPolygons */
596
        if (!poPolygonGeom || poPolygonGeom->IsEmpty() ||
597
            wkbFlatten(poPolygonGeom->getGeometryType()) != wkbPolygon)
598
        {
599
            CPLDebug("OGR-VFK", "%s: unable to polygonize (fid = %ld)",
600
                     m_pszName, iFID);
601
            nInvalidNoRings++;
602
            continue;
603
        }
604
605
        /* set polygon */
606
        poPolygonGeom->setCoordinateDimension(2); /* force 2D */
607
        if (!poFeature->SetGeometry(poPolygonGeom.get()))
608
        {
609
            nInvalidNoRings++;
610
            continue;
611
        }
612
613
        /* store also geometry in DB */
614
        if (poReader->IsSpatial() &&
615
            SaveGeometryToDB(poPolygonGeom.get(), rowId) != OGRERR_FAILURE)
616
            nGeometries++;
617
    }
618
619
    CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d", m_pszName,
620
             nInvalidNoLines, nInvalidNoRings);
621
622
    /* update number of geometries in VFK_DB_TABLE table */
623
    UpdateVfkBlocks(nGeometries);
624
625
    if (poReader->IsSpatial())
626
        poReader->ExecuteSQL("COMMIT");
627
628
    return nInvalidNoLines + nInvalidNoRings;
629
#endif  // HAVE_GEOS
630
98
}
631
632
/*!
633
  \brief Get feature by FID
634
635
  Modifies next feature id.
636
637
  \param nFID feature id
638
639
  \return pointer to feature definition or NULL on failure (not found)
640
*/
641
IVFKFeature *VFKDataBlockSQLite::GetFeature(GIntBig nFID)
642
0
{
643
0
    if (m_nFeatureCount < 0)
644
0
    {
645
0
        m_poReader->ReadDataRecords(this);
646
0
    }
647
648
0
    if (nFID < 1 || nFID > m_nFeatureCount)
649
0
        return nullptr;
650
651
0
    if (m_bGeometryPerBlock && !m_bGeometry)
652
0
    {
653
0
        LoadGeometry();
654
0
    }
655
656
0
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
657
658
0
    CPLString osSQL;
659
0
    osSQL.Printf("SELECT rowid FROM %s WHERE %s = " CPL_FRMT_GIB, m_pszName,
660
0
                 FID_COLUMN, nFID);
661
0
    if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
662
0
    {
663
0
        osSQL += " AND PORADOVE_CISLO_BODU = 1";
664
0
    }
665
0
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
666
667
0
    int rowId = -1;
668
0
    if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
669
0
    {
670
0
        rowId = sqlite3_column_int(hStmt, 0);
671
0
    }
672
0
    sqlite3_finalize(hStmt);
673
674
0
    return GetFeatureByIndex(rowId - 1);
675
0
}
676
677
/*!
678
  \brief Get first found feature based on its property
679
680
  \param column property name
681
  \param value property value
682
  \param bGeom True to check also geometry != NULL
683
684
  \return pointer to feature definition or NULL on failure (not found)
685
*/
686
VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column,
687
                                                 GUIntBig value, bool bGeom)
688
10.7k
{
689
10.7k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
690
691
10.7k
    CPLString osSQL;
692
10.7k
    osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB, FID_COLUMN,
693
10.7k
                 m_pszName, column, value);
694
10.7k
    if (bGeom)
695
0
    {
696
0
        CPLString osColumn;
697
698
0
        osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
699
0
        osSQL += osColumn;
700
0
    }
701
702
10.7k
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
703
10.7k
    if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
704
4.46k
        return nullptr;
705
706
6.25k
    const int idx = sqlite3_column_int(hStmt, 0) - 1;
707
6.25k
    sqlite3_finalize(hStmt);
708
709
6.25k
    if (idx < 0 || idx >= m_nFeatureCount)  // ? assert
710
17
        return nullptr;
711
712
6.24k
    return cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(idx));
713
6.25k
}
714
715
/*!
716
  \brief Get first found feature based on its properties (AND)
717
718
  \param column array of property names
719
  \param value array of property values
720
  \param num number of array items
721
  \param bGeom True to check also geometry != NULL
722
723
  \return pointer to feature definition or NULL on failure (not found)
724
*/
725
VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column,
726
                                                 GUIntBig *value, int num,
727
                                                 bool bGeom)
728
2.13k
{
729
2.13k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
730
731
2.13k
    CPLString osSQL;
732
2.13k
    osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName);
733
734
2.13k
    CPLString osItem;
735
6.39k
    for (int i = 0; i < num; i++)
736
4.26k
    {
737
4.26k
        if (i > 0)
738
2.13k
            osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]);
739
2.13k
        else
740
2.13k
            osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
741
4.26k
        osSQL += osItem;
742
4.26k
    }
743
2.13k
    if (bGeom)
744
2.13k
    {
745
2.13k
        osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
746
2.13k
        osSQL += osItem;
747
2.13k
    }
748
749
2.13k
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
750
2.13k
    if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
751
1.74k
        return nullptr;
752
753
382
    int idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */
754
382
    sqlite3_finalize(hStmt);
755
756
382
    if (idx < 0 || idx >= m_nFeatureCount)  // ? assert
757
0
        return nullptr;
758
759
382
    return cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(idx));
760
382
}
761
762
/*!
763
  \brief Get features based on properties
764
765
  \param column array of property names
766
  \param value array of property values
767
  \param num number of array items
768
769
  \return list of features
770
*/
771
VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column,
772
                                                     GUIntBig *value, int num)
773
0
{
774
0
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
775
776
0
    CPLString osItem;
777
0
    CPLString osSQL;
778
0
    osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName);
779
0
    for (int i = 0; i < num; i++)
780
0
    {
781
0
        if (i > 0)
782
0
            osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]);
783
0
        else
784
0
            osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
785
0
        osSQL += osItem;
786
0
    }
787
0
    osSQL += " ORDER BY ";
788
0
    osSQL += FID_COLUMN;
789
790
0
    VFKFeatureSQLiteList fList;
791
792
0
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
793
0
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
794
0
    {
795
0
        const int iRowId = sqlite3_column_int(hStmt, 0);
796
0
        VFKFeatureSQLite *poFeature =
797
0
            dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iRowId - 1));
798
0
        if (poFeature == nullptr)
799
0
        {
800
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve feature %d",
801
0
                     iRowId);
802
0
            sqlite3_finalize(hStmt);
803
0
            return VFKFeatureSQLiteList();
804
0
        }
805
0
        fList.push_back(poFeature);
806
0
    }
807
808
0
    return fList;
809
0
}
810
811
/*!
812
  \brief Save geometry to DB (as WKB)
813
814
  \param poGeom pointer to OGRGeometry to be saved
815
  \param iRowId row id to update
816
817
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
818
*/
819
OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom,
820
                                            int iRowId)
821
9.83k
{
822
9.83k
    int rc;
823
9.83k
    CPLString osSQL;
824
825
9.83k
    sqlite3_stmt *hStmt = nullptr;
826
827
9.83k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
828
829
    /* check if geometry column exists (see SUPPRESS_GEOMETRY open
830
       option) */
831
9.83k
    if (AddGeometryColumn() != OGRERR_NONE)
832
0
        return OGRERR_FAILURE;
833
834
9.83k
    if (poGeom)
835
9.83k
    {
836
9.83k
        const size_t nWKBLen = poGeom->WkbSize();
837
9.83k
        if (nWKBLen > static_cast<size_t>(std::numeric_limits<int>::max()))
838
0
        {
839
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
840
0
            return OGRERR_FAILURE;
841
0
        }
842
9.83k
        GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(nWKBLen);
843
9.83k
        if (pabyWKB)
844
9.83k
        {
845
9.83k
            poGeom->exportToWkb(wkbNDR, pabyWKB);
846
847
9.83k
            osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d", m_pszName,
848
9.83k
                         GEOM_COLUMN, iRowId);
849
9.83k
            hStmt = poReader->PrepareStatement(osSQL.c_str());
850
851
9.83k
            rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, static_cast<int>(nWKBLen),
852
9.83k
                                   CPLFree);
853
9.83k
            if (rc != SQLITE_OK)
854
0
            {
855
0
                sqlite3_finalize(hStmt);
856
0
                CPLError(CE_Failure, CPLE_AppDefined,
857
0
                         "Storing geometry in DB failed");
858
0
                return OGRERR_FAILURE;
859
0
            }
860
9.83k
        }
861
9.83k
    }
862
0
    else
863
0
    { /* invalid */
864
0
        osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d", m_pszName,
865
0
                     GEOM_COLUMN, iRowId);
866
0
        hStmt = poReader->PrepareStatement(osSQL.c_str());
867
0
    }
868
869
9.83k
    return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */
870
9.83k
}
871
872
/*!
873
  \brief Load geometry from DB
874
875
  \return true if geometry successfully loaded otherwise false
876
*/
877
bool VFKDataBlockSQLite::LoadGeometryFromDB()
878
821
{
879
821
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
880
881
821
    if (!poReader->IsSpatial()) /* check if DB is spatial */
882
0
        return false;
883
884
821
    CPLString osSQL;
885
821
    osSQL.Printf("SELECT num_geometries FROM %s WHERE table_name = '%s'",
886
821
                 VFK_DB_TABLE, m_pszName);
887
821
    sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
888
821
    if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
889
59
        return false;
890
762
    const int nGeometries = sqlite3_column_int(hStmt, 0);
891
762
    sqlite3_finalize(hStmt);
892
893
762
    if (nGeometries < 1)
894
623
        return false;
895
896
139
    const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
897
139
                              EQUAL(m_pszName, "OP") ||
898
139
                              EQUAL(m_pszName, "OBBP");
899
900
    /* load geometry from DB */
901
139
    osSQL.Printf("SELECT %s,rowid,%s FROM %s ", GEOM_COLUMN, FID_COLUMN,
902
139
                 m_pszName);
903
139
    if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
904
34
        osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
905
139
    osSQL += "ORDER BY ";
906
139
    osSQL += FID_COLUMN;
907
139
    hStmt = poReader->PrepareStatement(osSQL.c_str());
908
909
139
    int rowId = 0;
910
139
    int nInvalid = 0;
911
139
    int nGeometriesCount = 0;
912
913
6.12k
    while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
914
5.98k
    {
915
5.98k
        rowId++;  // =sqlite3_column_int(hStmt, 1);
916
5.98k
        const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
917
5.98k
        VFKFeatureSQLite *poFeature =
918
5.98k
            dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
919
5.98k
        if (poFeature == nullptr || poFeature->GetFID() != iFID)
920
701
        {
921
701
            continue;
922
701
        }
923
924
        // read geometry from DB
925
5.28k
        const int nBytes = sqlite3_column_bytes(hStmt, 0);
926
5.28k
        OGRGeometry *poGeometry = nullptr;
927
5.28k
        if (nBytes > 0 && OGRGeometryFactory::createFromWkb(
928
2.54k
                              sqlite3_column_blob(hStmt, 0), nullptr,
929
2.54k
                              &poGeometry, nBytes) == OGRERR_NONE)
930
2.54k
        {
931
2.54k
            nGeometriesCount++;
932
2.54k
            if (!poFeature->SetGeometry(poGeometry))
933
0
            {
934
0
                nInvalid++;
935
0
            }
936
2.54k
            delete poGeometry;
937
2.54k
        }
938
2.74k
        else
939
2.74k
        {
940
2.74k
            nInvalid++;
941
2.74k
        }
942
5.28k
    }
943
944
139
    CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB", m_pszName,
945
139
             nGeometriesCount);
946
947
139
    if (nGeometriesCount != nGeometries)
948
33
    {
949
33
        CPLError(CE_Warning, CPLE_AppDefined,
950
33
                 "%s: %d geometries loaded (should be %d)", m_pszName,
951
33
                 nGeometriesCount, nGeometries);
952
33
    }
953
954
139
    if (nInvalid > 0 && !bSkipInvalid)
955
101
    {
956
101
        CPLError(CE_Warning, CPLE_AppDefined,
957
101
                 "%s: %d features with invalid or empty geometry", m_pszName,
958
101
                 nInvalid);
959
101
    }
960
961
139
    return true;
962
762
}
963
964
/*!
965
  \brief Update VFK_DB_TABLE table
966
967
  \param nGeometries number of geometries to update
968
*/
969
void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries)
970
682
{
971
682
    CPLString osSQL;
972
973
682
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
974
975
    /* update number of features in VFK_DB_TABLE table */
976
682
    const int nFeatCount = (int)GetFeatureCount();
977
682
    if (nFeatCount > 0)
978
231
    {
979
231
        osSQL.Printf("UPDATE %s SET num_features = %d WHERE table_name = '%s'",
980
231
                     VFK_DB_TABLE, nFeatCount, m_pszName);
981
231
        poReader->ExecuteSQL(osSQL.c_str());
982
231
    }
983
984
    /* update number of geometries in VFK_DB_TABLE table */
985
682
    if (nGeometries > 0)
986
132
    {
987
132
        CPLDebug("OGR-VFK",
988
132
                 "VFKDataBlockSQLite::UpdateVfkBlocks(): name=%s -> "
989
132
                 "%d geometries saved to internal DB",
990
132
                 m_pszName, nGeometries);
991
992
132
        osSQL.Printf(
993
132
            "UPDATE %s SET num_geometries = %d WHERE table_name = '%s'",
994
132
            VFK_DB_TABLE, nGeometries, m_pszName);
995
132
        poReader->ExecuteSQL(osSQL.c_str());
996
132
    }
997
682
}
998
999
/*!
1000
  \brief Update feature id (see SBP)
1001
1002
  \param iFID feature id to set up
1003
  \param rowId list of rows to update
1004
*/
1005
void VFKDataBlockSQLite::UpdateFID(GIntBig iFID, const std::vector<int> &rowId)
1006
5.24k
{
1007
5.24k
    CPLString osSQL, osValue;
1008
5.24k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
1009
1010
    /* update number of geometries in VFK_DB_TABLE table */
1011
5.24k
    osSQL.Printf("UPDATE %s SET %s = " CPL_FRMT_GIB " WHERE rowid IN (",
1012
5.24k
                 m_pszName, FID_COLUMN, iFID);
1013
15.9k
    for (size_t i = 0; i < rowId.size(); i++)
1014
10.7k
    {
1015
10.7k
        if (i > 0)
1016
5.47k
            osValue.Printf(",%d", rowId[i]);
1017
5.24k
        else
1018
5.24k
            osValue.Printf("%d", rowId[i]);
1019
10.7k
        osSQL += osValue;
1020
10.7k
    }
1021
5.24k
    osSQL += ")";
1022
1023
5.24k
    poReader->ExecuteSQL(osSQL.c_str());
1024
5.24k
}
1025
1026
/*!
1027
  \brief Check is ring is closed
1028
1029
  \param poRing pointer to OGRLinearRing to check
1030
1031
  \return true if closed otherwise false
1032
*/
1033
bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing)
1034
0
{
1035
0
    const int nPoints = poRing->getNumPoints();
1036
0
    if (nPoints < 3)
1037
0
        return false;
1038
1039
0
    if (poRing->getX(0) == poRing->getX(nPoints - 1) &&
1040
0
        poRing->getY(0) == poRing->getY(nPoints - 1))
1041
0
        return true;
1042
1043
0
    return false;
1044
0
}
1045
1046
/*!
1047
  \brief Get primary key
1048
1049
  \return property name or NULL
1050
*/
1051
const char *VFKDataBlockSQLite::GetKey() const
1052
858
{
1053
858
    if (GetPropertyCount() > 1)
1054
649
    {
1055
649
        const VFKPropertyDefn *poPropDefn = GetProperty(0);
1056
649
        const char *pszKey = poPropDefn->GetName();
1057
649
        if (EQUAL(pszKey, "ID"))
1058
604
            return pszKey;
1059
649
    }
1060
1061
254
    return nullptr;
1062
858
}
1063
1064
/*!
1065
  \brief Get geometry SQL type (for geometry_columns table)
1066
1067
  \return geometry_type as integer
1068
*/
1069
int VFKDataBlockSQLite::GetGeometrySQLType() const
1070
2.34k
{
1071
2.34k
    if (m_nGeometryType == wkbPolygon)
1072
52
        return 3;
1073
2.29k
    else if (m_nGeometryType == wkbLineString)
1074
178
        return 2;
1075
2.11k
    else if (m_nGeometryType == wkbPoint)
1076
208
        return 1;
1077
1078
1.90k
    return 0; /* unknown geometry type */
1079
2.34k
}
1080
1081
/*!
1082
  \brief Add geometry column into table if not exists
1083
1084
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1085
 */
1086
OGRErr VFKDataBlockSQLite::AddGeometryColumn() const
1087
10.3k
{
1088
10.3k
    CPLString osSQL;
1089
1090
10.3k
    VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
1091
1092
10.3k
    osSQL.Printf("SELECT %s FROM %s LIMIT 0", GEOM_COLUMN, m_pszName);
1093
10.3k
    if (poReader->ExecuteSQL(osSQL.c_str(), CE_None) == OGRERR_FAILURE)
1094
151
    {
1095
        /* query failed, we assume that geometry column not exists */
1096
151
        osSQL.Printf("ALTER TABLE %s ADD COLUMN %s blob", m_pszName,
1097
151
                     GEOM_COLUMN);
1098
151
        return poReader->ExecuteSQL(osSQL.c_str());
1099
151
    }
1100
1101
10.1k
    return OGRERR_NONE;
1102
10.3k
}
1103
1104
/*!
1105
  \brief Load feature properties
1106
1107
  Used for sequential access, see OGRVFKLayer:GetNextFeature().
1108
1109
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1110
*/
1111
OGRErr VFKDataBlockSQLite::LoadProperties()
1112
5.64k
{
1113
5.64k
    CPLString osSQL;
1114
1115
5.64k
    if (m_hStmt)
1116
0
        sqlite3_finalize(m_hStmt);
1117
1118
5.64k
    osSQL.Printf("SELECT * FROM %s",  // TODO: where
1119
5.64k
                 m_pszName);
1120
5.64k
    if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
1121
205
        osSQL += " WHERE PORADOVE_CISLO_BODU = 1";
1122
1123
5.64k
    m_hStmt = cpl::down_cast<VFKReaderSQLite *>(m_poReader)
1124
5.64k
                  ->PrepareStatement(osSQL.c_str());
1125
1126
5.64k
    if (m_hStmt == nullptr)
1127
4.11k
        return OGRERR_FAILURE;
1128
1129
1.52k
    return OGRERR_NONE;
1130
5.64k
}
1131
1132
/*
1133
  \brief Clean feature properties for a next run
1134
1135
  \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1136
*/
1137
OGRErr VFKDataBlockSQLite::CleanProperties()
1138
16.0k
{
1139
16.0k
    if (m_hStmt)
1140
1.52k
    {
1141
1.52k
        if (sqlite3_finalize(m_hStmt) != SQLITE_OK)
1142
0
        {
1143
0
            m_hStmt = nullptr;
1144
0
            return OGRERR_FAILURE;
1145
0
        }
1146
1.52k
        m_hStmt = nullptr;
1147
1.52k
    }
1148
1149
16.0k
    return OGRERR_NONE;
1150
16.0k
}