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/gml/hugefileresolver.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GML Reader
4
 * Purpose:  Implementation of GMLReader::HugeFileResolver() method.
5
 * Author:   Alessandro Furieri, a.furitier@lqt.it
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2011, Alessandro Furieri
9
 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 *
13
 ******************************************************************************
14
 * Contributor: Alessandro Furieri, a.furieri@lqt.it
15
 * This module implements GML_SKIP_RESOLVE_ELEMS HUGE
16
 * Developed for Faunalia ( http://www.faunalia.it) with funding from
17
 * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE
18
 *
19
 ****************************************************************************/
20
21
#include "cpl_port.h"
22
#include "gmlreader.h"
23
#include "gmlreaderp.h"
24
25
#include <algorithm>
26
#include <cmath>
27
#include <limits>
28
29
#include "cpl_conv.h"
30
#include "cpl_error.h"
31
#include "cpl_http.h"
32
#include "cpl_string.h"
33
#include "gmlutils.h"
34
#include "ogr_p.h"
35
36
#ifdef HAVE_SQLITE
37
#include <sqlite3.h>
38
39
#undef SQLITE_STATIC
40
0
#define SQLITE_STATIC static_cast<sqlite3_destructor_type>(nullptr)
41
42
#endif
43
44
/****************************************************/
45
/*      SQLite is absolutely required in order to   */
46
/*      support the HUGE xlink:href resolver        */
47
/****************************************************/
48
49
#ifdef HAVE_SQLITE
50
51
// Internal helper struct supporting GML tags <Edge>.
52
struct huge_tag
53
{
54
    CPLString *gmlTagValue;
55
    CPLString *gmlId;
56
    CPLString *gmlNodeFrom;
57
    CPLString *gmlNodeTo;
58
    bool bIsNodeFromHref;
59
    bool bIsNodeToHref;
60
    bool bHasCoords;
61
    bool bHasZ;
62
    double xNodeFrom;
63
    double yNodeFrom;
64
    double zNodeFrom;
65
    double xNodeTo;
66
    double yNodeTo;
67
    double zNodeTo;
68
    struct huge_tag *pNext;
69
};
70
71
// Internal helper struct supporting GML tags xlink:href.
72
struct huge_href
73
{
74
    CPLString *gmlId;
75
    CPLString *gmlText;
76
    const CPLXMLNode *psParent;
77
    const CPLXMLNode *psNode;
78
    // bool                bIsDirectedEdge;
79
    char cOrientation;
80
    struct huge_href *pNext;
81
};
82
83
// Internal struct supporting GML rewriting.
84
struct huge_child
85
{
86
    CPLXMLNode *psChild;
87
    struct huge_href *pItem;
88
    struct huge_child *pNext;
89
};
90
91
// Internal struct supporting GML rewriting.
92
struct huge_parent
93
{
94
    CPLXMLNode *psParent;
95
    struct huge_child *pFirst;
96
    // cppcheck-suppress unusedStructMember
97
    struct huge_child *pLast;
98
    struct huge_parent *pNext;
99
};
100
101
// Internal class supporting GML resolver for Huge Files (based on SQLite).
102
class huge_helper
103
{
104
  public:
105
0
    huge_helper() = default;
106
    huge_helper(const huge_helper &) = delete;
107
    huge_helper &operator=(const huge_helper &) = delete;
108
109
    sqlite3 *hDB = nullptr;
110
    sqlite3_stmt *hNodes = nullptr;
111
    sqlite3_stmt *hEdges = nullptr;
112
    CPLString *nodeSrs = nullptr;
113
    struct huge_tag *pFirst = nullptr;
114
    struct huge_tag *pLast = nullptr;
115
    struct huge_href *pFirstHref = nullptr;
116
    struct huge_href *pLastHref = nullptr;
117
    struct huge_parent *pFirstParent = nullptr;
118
    struct huge_parent *pLastParent = nullptr;
119
    std::unique_ptr<OGRGML_SRSCache, decltype(&OGRGML_SRSCache_Destroy)>
120
        srsCache{OGRGML_SRSCache_Create(), OGRGML_SRSCache_Destroy};
121
};
122
123
static bool gmlHugeFileSQLiteInit(huge_helper *helper)
124
0
{
125
    // Attempting to create SQLite tables.
126
0
    char *pszErrMsg = nullptr;
127
0
    sqlite3 *hDB = helper->hDB;
128
129
    // DB table: NODES.
130
0
    {
131
0
        const char osCommand[] = "CREATE TABLE nodes ("
132
0
                                 "     gml_id VARCHAR PRIMARY KEY, "
133
0
                                 "     x DOUBLE, "
134
0
                                 "     y DOUBLE, "
135
0
                                 "     z DOUBLE)";
136
0
        const int rc =
137
0
            sqlite3_exec(hDB, osCommand, nullptr, nullptr, &pszErrMsg);
138
0
        if (rc != SQLITE_OK)
139
0
        {
140
0
            CPLError(CE_Failure, CPLE_AppDefined,
141
0
                     "Unable to create table nodes: %s", pszErrMsg);
142
0
            sqlite3_free(pszErrMsg);
143
0
            return false;
144
0
        }
145
0
    }
146
147
    // DB table: GML_EDGES.
148
0
    {
149
0
        const char osCommand[] = "CREATE TABLE gml_edges ("
150
0
                                 "     gml_id VARCHAR PRIMARY KEY, "
151
0
                                 "     gml_string TEXT, "
152
0
                                 "     gml_resolved TEXT, "
153
0
                                 "     node_from_id TEXT, "
154
0
                                 "     node_from_x DOUBLE, "
155
0
                                 "     node_from_y DOUBLE, "
156
0
                                 "     node_from_z DOUBLE, "
157
0
                                 "     node_to_id TEXT, "
158
0
                                 "     node_to_x DOUBLE, "
159
0
                                 "     node_to_y DOUBLE, "
160
0
                                 "     node_to_z DOUBLE)";
161
0
        const int rc =
162
0
            sqlite3_exec(hDB, osCommand, nullptr, nullptr, &pszErrMsg);
163
0
        if (rc != SQLITE_OK)
164
0
        {
165
0
            CPLError(CE_Failure, CPLE_AppDefined,
166
0
                     "Unable to create table gml_edges: %s", pszErrMsg);
167
0
            sqlite3_free(pszErrMsg);
168
0
            return false;
169
0
        }
170
0
    }
171
172
    // DB table: NODES / Insert cursor.
173
0
    {
174
0
        const char osCommand[] =
175
0
            "INSERT OR IGNORE INTO nodes (gml_id, x, y, z) VALUES (?, ?, ?, ?)";
176
0
        sqlite3_stmt *hStmt = nullptr;
177
0
        const int rc = sqlite3_prepare_v2(hDB, osCommand, -1, &hStmt, nullptr);
178
0
        if (rc != SQLITE_OK)
179
0
        {
180
0
            CPLError(CE_Failure, CPLE_AppDefined,
181
0
                     "Unable to create INSERT stmt for: nodes");
182
0
            return false;
183
0
        }
184
0
        helper->hNodes = hStmt;
185
0
    }
186
187
    // DB table: GML_EDGES / Insert cursor.
188
0
    {
189
0
        const char osCommand[] = "INSERT INTO gml_edges "
190
0
                                 "(gml_id, gml_string, gml_resolved, "
191
0
                                 "node_from_id, node_from_x, node_from_y, "
192
0
                                 "node_from_z, node_to_id, node_to_x, "
193
0
                                 "node_to_y, node_to_z) "
194
0
                                 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
195
0
        sqlite3_stmt *hStmt = nullptr;
196
0
        const int rc = sqlite3_prepare_v2(hDB, osCommand, -1, &hStmt, nullptr);
197
0
        if (rc != SQLITE_OK)
198
0
        {
199
0
            CPLError(CE_Failure, CPLE_AppDefined,
200
0
                     "Unable to create INSERT stmt for: gml_edges");
201
0
            return false;
202
0
        }
203
0
        helper->hEdges = hStmt;
204
0
    }
205
206
    // Starting a TRANSACTION.
207
0
    const int rc = sqlite3_exec(hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
208
0
    if (rc != SQLITE_OK)
209
0
    {
210
0
        CPLError(CE_Failure, CPLE_AppDefined,
211
0
                 "Unable to perform BEGIN TRANSACTION: %s", pszErrMsg);
212
0
        sqlite3_free(pszErrMsg);
213
0
        return false;
214
0
    }
215
216
0
    return true;
217
0
}
218
219
static bool gmlHugeResolveEdgeNodes(const CPLXMLNode *psNode,
220
                                    const char *pszFromId, const char *pszToId)
221
0
{
222
0
    if (psNode->eType != CXT_Element || !EQUAL(psNode->pszValue, "Edge"))
223
0
    {
224
0
        return false;
225
0
    }
226
227
    // Resolves an Edge definition.
228
0
    CPLXMLNode *psDirNode_1 = nullptr;
229
0
    CPLXMLNode *psDirNode_2 = nullptr;
230
0
    CPLXMLNode *psOldNode_1 = nullptr;
231
0
    CPLXMLNode *psOldNode_2 = nullptr;
232
0
    CPLXMLNode *psNewNode_1 = nullptr;
233
0
    CPLXMLNode *psNewNode_2 = nullptr;
234
0
    int iToBeReplaced = 0;
235
0
    int iReplaced = 0;
236
237
0
    CPLXMLNode *psChild = psNode->psChild;
238
0
    while (psChild != nullptr)
239
0
    {
240
0
        if (psChild->eType == CXT_Element &&
241
0
            EQUAL(psChild->pszValue, "directedNode"))
242
0
        {
243
0
            char cOrientation = '+';
244
0
            CPLXMLNode *psOldNode = nullptr;
245
0
            CPLXMLNode *psAttr = psChild->psChild;
246
0
            while (psAttr != nullptr)
247
0
            {
248
0
                if (psAttr->eType == CXT_Attribute &&
249
0
                    EQUAL(psAttr->pszValue, "xlink:href"))
250
0
                    psOldNode = psAttr;
251
0
                if (psAttr->eType == CXT_Attribute &&
252
0
                    EQUAL(psAttr->pszValue, "orientation"))
253
0
                {
254
0
                    const CPLXMLNode *psOrientation = psAttr->psChild;
255
0
                    if (psOrientation != nullptr)
256
0
                    {
257
0
                        if (psOrientation->eType == CXT_Text)
258
0
                            cOrientation = *(psOrientation->pszValue);
259
0
                    }
260
0
                }
261
0
                psAttr = psAttr->psNext;
262
0
            }
263
0
            if (psOldNode != nullptr)
264
0
            {
265
0
                CPLXMLNode *psNewNode =
266
0
                    CPLCreateXMLNode(nullptr, CXT_Element, "Node");
267
0
                CPLXMLNode *psGMLIdNode =
268
0
                    CPLCreateXMLNode(psNewNode, CXT_Attribute, "gml:id");
269
0
                if (cOrientation == '-')
270
0
                    CPLCreateXMLNode(psGMLIdNode, CXT_Text, pszFromId);
271
0
                else
272
0
                    CPLCreateXMLNode(psGMLIdNode, CXT_Text, pszToId);
273
0
                if (iToBeReplaced == 0)
274
0
                {
275
0
                    psDirNode_1 = psChild;
276
0
                    psOldNode_1 = psOldNode;
277
0
                    psNewNode_1 = psNewNode;
278
0
                }
279
0
                else
280
0
                {
281
0
                    psDirNode_2 = psChild;
282
0
                    psOldNode_2 = psOldNode;
283
0
                    psNewNode_2 = psNewNode;
284
0
                }
285
0
                iToBeReplaced++;
286
0
            }
287
0
        }
288
0
        psChild = psChild->psNext;
289
0
    }
290
291
    // Rewriting the Edge GML definition.
292
0
    if (psDirNode_1 != nullptr)
293
0
    {
294
0
        if (psOldNode_1 != nullptr)
295
0
        {
296
0
            CPLRemoveXMLChild(psDirNode_1, psOldNode_1);
297
0
            CPLDestroyXMLNode(psOldNode_1);
298
0
            if (psNewNode_1 != nullptr)
299
0
            {
300
0
                CPLAddXMLChild(psDirNode_1, psNewNode_1);
301
0
                iReplaced++;
302
0
            }
303
0
        }
304
0
    }
305
0
    if (psDirNode_2 != nullptr)
306
0
    {
307
0
        if (psOldNode_2 != nullptr)
308
0
        {
309
0
            CPLRemoveXMLChild(psDirNode_2, psOldNode_2);
310
0
            CPLDestroyXMLNode(psOldNode_2);
311
0
            if (psNewNode_2 != nullptr)
312
0
            {
313
0
                CPLAddXMLChild(psDirNode_2, psNewNode_2);
314
0
                iReplaced++;
315
0
            }
316
0
        }
317
0
    }
318
319
0
    return iToBeReplaced == iReplaced;
320
0
}
321
322
static bool gmlHugeFileResolveEdges(huge_helper *helper)
323
0
{
324
    // Identifying any not yet resolved <Edge> GML string.
325
0
    sqlite3 *hDB = helper->hDB;
326
327
    // Query cursor.
328
0
    sqlite3_stmt *hQueryStmt = nullptr;
329
0
    {
330
0
        const char osCommand[] =
331
0
            "SELECT e.gml_id, e.gml_string, e.node_from_id, "
332
0
            "e.node_from_x, e.node_from_y, e.node_from_z, "
333
0
            "n1.gml_id, n1.x, n1.y, n1.z, e.node_to_id, "
334
0
            "e.node_to_x, e.node_to_y, e.node_to_z, "
335
0
            "n2.gml_id, n2.x, n2.y, n2.z "
336
0
            "FROM gml_edges AS e "
337
0
            "LEFT JOIN nodes AS n1 ON (n1.gml_id = e.node_from_id) "
338
0
            "LEFT JOIN nodes AS n2 ON (n2.gml_id = e.node_to_id)";
339
0
        const int rc =
340
0
            sqlite3_prepare_v2(hDB, osCommand, -1, &hQueryStmt, nullptr);
341
0
        if (rc != SQLITE_OK)
342
0
        {
343
0
            CPLError(CE_Failure, CPLE_AppDefined,
344
0
                     "Unable to create QUERY stmt for Edge resolver");
345
0
            return false;
346
0
        }
347
0
    }
348
349
    // Update cursor.
350
0
    sqlite3_stmt *hUpdateStmt = nullptr;
351
0
    {
352
0
        const char osCommand[] = "UPDATE gml_edges "
353
0
                                 "SET gml_resolved = ?, "
354
0
                                 "gml_string = NULL "
355
0
                                 "WHERE gml_id = ?";
356
0
        const int rc =
357
0
            sqlite3_prepare_v2(hDB, osCommand, -1, &hUpdateStmt, nullptr);
358
0
        if (rc != SQLITE_OK)
359
0
        {
360
0
            CPLError(CE_Failure, CPLE_AppDefined,
361
0
                     "Unable to create UPDATE stmt for resolved Edges");
362
0
            sqlite3_finalize(hQueryStmt);
363
0
            return false;
364
0
        }
365
0
    }
366
367
    // Starting a TRANSACTION.
368
0
    char *pszErrMsg = nullptr;
369
0
    {
370
0
        const int rc = sqlite3_exec(hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
371
0
        if (rc != SQLITE_OK)
372
0
        {
373
0
            CPLError(CE_Failure, CPLE_AppDefined,
374
0
                     "Unable to perform BEGIN TRANSACTION: %s", pszErrMsg);
375
0
            sqlite3_free(pszErrMsg);
376
0
            sqlite3_finalize(hQueryStmt);
377
0
            sqlite3_finalize(hUpdateStmt);
378
0
            return false;
379
0
        }
380
0
    }
381
382
0
    int iCount = 0;
383
0
    bool bError = false;
384
385
    // Looping on the QUERY result-set.
386
0
    while (true)
387
0
    {
388
0
        const char *pszGmlId = nullptr;
389
0
        const char *pszGmlString = nullptr;
390
0
        const char *pszFromId = nullptr;
391
0
        double xFrom = std::numeric_limits<double>::quiet_NaN();
392
0
        double yFrom = std::numeric_limits<double>::quiet_NaN();
393
0
        double zFrom = std::numeric_limits<double>::quiet_NaN();
394
0
        const char *pszNodeFromId = nullptr;
395
0
        double xNodeFrom = std::numeric_limits<double>::quiet_NaN();
396
0
        double yNodeFrom = std::numeric_limits<double>::quiet_NaN();
397
0
        double zNodeFrom = std::numeric_limits<double>::quiet_NaN();
398
0
        const char *pszToId = nullptr;
399
0
        double xTo = std::numeric_limits<double>::quiet_NaN();
400
0
        double yTo = std::numeric_limits<double>::quiet_NaN();
401
0
        double zTo = std::numeric_limits<double>::quiet_NaN();
402
0
        const char *pszNodeToId = nullptr;
403
0
        double xNodeTo = std::numeric_limits<double>::quiet_NaN();
404
0
        double yNodeTo = std::numeric_limits<double>::quiet_NaN();
405
0
        double zNodeTo = std::numeric_limits<double>::quiet_NaN();
406
407
0
        const int rc2 = sqlite3_step(hQueryStmt);
408
0
        if (rc2 == SQLITE_DONE)
409
0
            break;
410
411
0
        if (rc2 == SQLITE_ROW)
412
0
        {
413
0
            bError = false;
414
0
            pszGmlId = reinterpret_cast<const char *>(
415
0
                sqlite3_column_text(hQueryStmt, 0));
416
0
            if (sqlite3_column_type(hQueryStmt, 1) == SQLITE_TEXT)
417
0
            {
418
0
                pszGmlString = reinterpret_cast<const char *>(
419
0
                    sqlite3_column_text(hQueryStmt, 1));
420
0
            }
421
0
            if (sqlite3_column_type(hQueryStmt, 2) != SQLITE_NULL)
422
0
            {
423
0
                pszFromId = reinterpret_cast<const char *>(
424
0
                    sqlite3_column_text(hQueryStmt, 2));
425
0
            }
426
0
            if (sqlite3_column_type(hQueryStmt, 3) != SQLITE_NULL)
427
0
            {
428
0
                xFrom = sqlite3_column_double(hQueryStmt, 3);
429
0
            }
430
0
            if (sqlite3_column_type(hQueryStmt, 4) != SQLITE_NULL)
431
0
            {
432
0
                yFrom = sqlite3_column_double(hQueryStmt, 4);
433
0
            }
434
0
            if (sqlite3_column_type(hQueryStmt, 5) != SQLITE_NULL)
435
0
            {
436
0
                zFrom = sqlite3_column_double(hQueryStmt, 5);
437
0
            }
438
0
            if (sqlite3_column_type(hQueryStmt, 6) != SQLITE_NULL)
439
0
            {
440
0
                pszNodeFromId = reinterpret_cast<const char *>(
441
0
                    sqlite3_column_text(hQueryStmt, 6));
442
0
            }
443
0
            if (sqlite3_column_type(hQueryStmt, 7) != SQLITE_NULL)
444
0
            {
445
0
                xNodeFrom = sqlite3_column_double(hQueryStmt, 7);
446
0
            }
447
0
            if (sqlite3_column_type(hQueryStmt, 8) != SQLITE_NULL)
448
0
            {
449
0
                yNodeFrom = sqlite3_column_double(hQueryStmt, 8);
450
0
            }
451
0
            if (sqlite3_column_type(hQueryStmt, 9) != SQLITE_NULL)
452
0
            {
453
0
                zNodeFrom = sqlite3_column_double(hQueryStmt, 9);
454
0
            }
455
0
            if (sqlite3_column_type(hQueryStmt, 10) != SQLITE_NULL)
456
0
            {
457
0
                pszToId = reinterpret_cast<const char *>(
458
0
                    sqlite3_column_text(hQueryStmt, 10));
459
0
            }
460
0
            if (sqlite3_column_type(hQueryStmt, 11) != SQLITE_NULL)
461
0
            {
462
0
                xTo = sqlite3_column_double(hQueryStmt, 11);
463
0
            }
464
0
            if (sqlite3_column_type(hQueryStmt, 12) != SQLITE_NULL)
465
0
            {
466
0
                yTo = sqlite3_column_double(hQueryStmt, 12);
467
0
            }
468
0
            if (sqlite3_column_type(hQueryStmt, 13) != SQLITE_NULL)
469
0
            {
470
0
                zTo = sqlite3_column_double(hQueryStmt, 13);
471
0
            }
472
0
            if (sqlite3_column_type(hQueryStmt, 14) != SQLITE_NULL)
473
0
            {
474
0
                pszNodeToId = reinterpret_cast<const char *>(
475
0
                    sqlite3_column_text(hQueryStmt, 14));
476
0
            }
477
0
            if (sqlite3_column_type(hQueryStmt, 15) != SQLITE_NULL)
478
0
            {
479
0
                xNodeTo = sqlite3_column_double(hQueryStmt, 15);
480
0
            }
481
0
            if (sqlite3_column_type(hQueryStmt, 16) != SQLITE_NULL)
482
0
            {
483
0
                yNodeTo = sqlite3_column_double(hQueryStmt, 16);
484
0
            }
485
0
            if (sqlite3_column_type(hQueryStmt, 17) != SQLITE_NULL)
486
0
            {
487
0
                zNodeTo = sqlite3_column_double(hQueryStmt, 17);
488
0
            }
489
490
            // Checking for consistency.
491
0
            if (pszFromId == nullptr || std::isnan(xFrom) || std::isnan(yFrom))
492
0
            {
493
0
                bError = true;
494
0
                CPLError(CE_Failure, CPLE_AppDefined,
495
0
                         "Edge gml:id=\"%s\": invalid Node-from", pszGmlId);
496
0
            }
497
0
            else
498
0
            {
499
0
                if (pszNodeFromId == nullptr)
500
0
                {
501
0
                    bError = true;
502
0
                    CPLError(
503
0
                        CE_Failure, CPLE_AppDefined,
504
0
                        "Edge gml:id=\"%s\": undeclared Node gml:id=\"%s\"",
505
0
                        pszGmlId, pszFromId);
506
0
                }
507
0
                else if (std::isnan(xNodeFrom) || std::isnan(yNodeFrom))
508
0
                {
509
0
                    bError = true;
510
0
                    CPLError(CE_Failure, CPLE_AppDefined,
511
0
                             "Edge gml:id=\"%s\": "
512
0
                             "unknown coords for Node gml:id=\"%s\"",
513
0
                             pszGmlId, pszFromId);
514
0
                }
515
0
                else if (xFrom != xNodeFrom || yFrom != yNodeFrom)
516
0
                {
517
0
                    bError = true;
518
0
                    CPLError(CE_Failure, CPLE_AppDefined,
519
0
                             "Edge gml:id=\"%s\": mismatching coords for Node "
520
0
                             "gml:id=\"%s\"",
521
0
                             pszGmlId, pszFromId);
522
0
                }
523
0
                else
524
0
                {
525
0
                    if (std::isnan(zFrom) && std::isnan(zNodeFrom))
526
0
                    {
527
0
                        ;
528
0
                    }
529
0
                    else if (std::isnan(zFrom) || std::isnan(zNodeFrom))
530
0
                    {
531
0
                        bError = true;
532
0
                        CPLError(CE_Failure, CPLE_AppDefined,
533
0
                                 "Edge gml:id=\"%s\": mismatching 2D/3D for "
534
0
                                 "Node gml:id=\"%s\"",
535
0
                                 pszGmlId, pszFromId);
536
0
                    }
537
0
                    else if (zFrom != zNodeFrom)
538
0
                    {
539
0
                        bError = true;
540
0
                        CPLError(CE_Failure, CPLE_AppDefined,
541
0
                                 "Edge gml:id=\"%s\": mismatching Z coord for "
542
0
                                 "Node gml:id=\"%s\"",
543
0
                                 pszGmlId, pszFromId);
544
0
                    }
545
0
                }
546
0
            }
547
0
            if (pszToId == nullptr || std::isnan(xTo) || std::isnan(yTo))
548
0
            {
549
0
                bError = true;
550
0
                CPLError(CE_Failure, CPLE_AppDefined,
551
0
                         "Edge gml:id=\"%s\": invalid Node-to", pszGmlId);
552
0
            }
553
0
            else
554
0
            {
555
0
                if (pszNodeToId == nullptr)
556
0
                {
557
0
                    bError = true;
558
0
                    CPLError(
559
0
                        CE_Failure, CPLE_AppDefined,
560
0
                        "Edge gml:id=\"%s\": undeclared Node gml:id=\"%s\"",
561
0
                        pszGmlId, pszToId);
562
0
                }
563
0
                else if (std::isnan(xNodeTo) || std::isnan(yNodeTo))
564
0
                {
565
0
                    bError = true;
566
0
                    CPLError(CE_Failure, CPLE_AppDefined,
567
0
                             "Edge gml:id=\"%s\": "
568
0
                             "unknown coords for Node gml:id=\"%s\"",
569
0
                             pszGmlId, pszToId);
570
0
                }
571
0
                else if (xTo != xNodeTo || yTo != yNodeTo)
572
0
                {
573
0
                    bError = true;
574
0
                    CPLError(CE_Failure, CPLE_AppDefined,
575
0
                             "Edge gml:id=\"%s\": mismatching coords for Node "
576
0
                             "gml:id=\"%s\"",
577
0
                             pszGmlId, pszToId);
578
0
                }
579
0
                else
580
0
                {
581
0
                    if (std::isnan(zTo) && std::isnan(zNodeTo))
582
0
                    {
583
0
                        ;
584
0
                    }
585
0
                    else if (std::isnan(zTo) || std::isnan(zNodeTo))
586
0
                    {
587
0
                        bError = true;
588
0
                        CPLError(CE_Failure, CPLE_AppDefined,
589
0
                                 "Edge gml:id=\"%s\": mismatching 2D/3D for "
590
0
                                 "Node gml:id=\"%s\"",
591
0
                                 pszGmlId, pszToId);
592
0
                    }
593
0
                    else if (zTo != zNodeTo)
594
0
                    {
595
0
                        bError = true;
596
0
                        CPLError(CE_Failure, CPLE_AppDefined,
597
0
                                 "Edge gml:id=\"%s\": mismatching Z coord for "
598
0
                                 "Node gml:id=\"%s\"",
599
0
                                 pszGmlId, pszToId);
600
0
                    }
601
0
                }
602
0
            }
603
604
            // Updating the resolved Node.
605
0
            if (bError == false && pszGmlString != nullptr &&
606
0
                pszFromId != nullptr && pszToId != nullptr)
607
0
            {
608
                // coverity[tainted_data]
609
0
                CPLXMLNode *psNode = CPLParseXMLString(pszGmlString);
610
0
                if (psNode != nullptr)
611
0
                {
612
0
                    if (gmlHugeResolveEdgeNodes(psNode, pszFromId, pszToId))
613
0
                    {
614
0
                        char *gmlText = CPLSerializeXMLTree(psNode);
615
0
                        sqlite3_reset(hUpdateStmt);
616
0
                        sqlite3_clear_bindings(hUpdateStmt);
617
0
                        sqlite3_bind_text(hUpdateStmt, 1, gmlText,
618
0
                                          static_cast<int>(strlen(gmlText)),
619
0
                                          SQLITE_STATIC);
620
0
                        sqlite3_bind_text(hUpdateStmt, 2, pszGmlId, -1,
621
0
                                          SQLITE_STATIC);
622
0
                        {
623
0
                            const int rc = sqlite3_step(hUpdateStmt);
624
0
                            if (rc != SQLITE_OK && rc != SQLITE_DONE)
625
0
                            {
626
0
                                CPLError(CE_Failure, CPLE_AppDefined,
627
0
                                         "UPDATE resolved Edge \"%s\" "
628
0
                                         "sqlite3_step() failed:\n  %s",
629
0
                                         pszGmlId, sqlite3_errmsg(hDB));
630
0
                            }
631
0
                        }
632
0
                        CPLFree(gmlText);
633
0
                        iCount++;
634
0
                        if ((iCount % 1024) == 1023)
635
0
                        {
636
                            // Committing the current TRANSACTION.
637
0
                            const int rc3 = sqlite3_exec(hDB, "COMMIT", nullptr,
638
0
                                                         nullptr, &pszErrMsg);
639
0
                            if (rc3 != SQLITE_OK)
640
0
                            {
641
0
                                CPLError(
642
0
                                    CE_Failure, CPLE_AppDefined,
643
0
                                    "Unable to perform COMMIT TRANSACTION: %s",
644
0
                                    pszErrMsg);
645
0
                                sqlite3_free(pszErrMsg);
646
0
                                return false;
647
0
                            }
648
                            // Restarting a new TRANSACTION.
649
0
                            const int rc4 = sqlite3_exec(hDB, "BEGIN", nullptr,
650
0
                                                         nullptr, &pszErrMsg);
651
0
                            if (rc4 != SQLITE_OK)
652
0
                            {
653
0
                                CPLError(
654
0
                                    CE_Failure, CPLE_AppDefined,
655
0
                                    "Unable to perform BEGIN TRANSACTION: %s",
656
0
                                    pszErrMsg);
657
0
                                sqlite3_free(pszErrMsg);
658
0
                                sqlite3_finalize(hQueryStmt);
659
0
                                sqlite3_finalize(hUpdateStmt);
660
0
                                return false;
661
0
                            }
662
0
                        }
663
0
                    }
664
0
                    CPLDestroyXMLNode(psNode);
665
0
                }
666
0
            }
667
0
        }
668
0
        else
669
0
        {
670
0
            CPLError(CE_Failure, CPLE_AppDefined,
671
0
                     "Edge resolver QUERY: sqlite3_step(%s)",
672
0
                     sqlite3_errmsg(hDB));
673
0
            sqlite3_finalize(hQueryStmt);
674
0
            sqlite3_finalize(hUpdateStmt);
675
0
            return false;
676
0
        }
677
0
    }
678
0
    sqlite3_finalize(hQueryStmt);
679
0
    sqlite3_finalize(hUpdateStmt);
680
681
    // Committing the current TRANSACTION.
682
0
    const int rc = sqlite3_exec(hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
683
0
    if (rc != SQLITE_OK)
684
0
    {
685
0
        CPLError(CE_Failure, CPLE_AppDefined,
686
0
                 "Unable to perform COMMIT TRANSACTION: %s", pszErrMsg);
687
0
        sqlite3_free(pszErrMsg);
688
0
        return false;
689
0
    }
690
691
0
    return !bError;
692
0
}
693
694
static bool gmlHugeFileSQLiteInsert(huge_helper *helper)
695
0
{
696
    // Inserting any appropriate row into the SQLite DB.
697
698
    // Looping on GML tags.
699
0
    struct huge_tag *pItem = helper->pFirst;
700
0
    while (pItem != nullptr)
701
0
    {
702
0
        if (pItem->bHasCoords)
703
0
        {
704
0
            if (pItem->gmlNodeFrom != nullptr)
705
0
            {
706
0
                sqlite3_reset(helper->hNodes);
707
0
                sqlite3_clear_bindings(helper->hNodes);
708
0
                sqlite3_bind_text(helper->hNodes, 1,
709
0
                                  pItem->gmlNodeFrom->c_str(), -1,
710
0
                                  SQLITE_STATIC);
711
0
                sqlite3_bind_double(helper->hNodes, 2, pItem->xNodeFrom);
712
0
                sqlite3_bind_double(helper->hNodes, 3, pItem->yNodeFrom);
713
0
                if (pItem->bHasZ)
714
0
                    sqlite3_bind_double(helper->hNodes, 4, pItem->zNodeFrom);
715
0
                sqlite3_bind_null(helper->hNodes, 5);
716
0
                const int rc = sqlite3_step(helper->hNodes);
717
0
                if (rc != SQLITE_OK && rc != SQLITE_DONE)
718
0
                {
719
0
                    CPLError(CE_Failure, CPLE_AppDefined,
720
0
                             "sqlite3_step() failed:\n  %s (gmlNodeFrom id=%s)",
721
0
                             sqlite3_errmsg(helper->hDB),
722
0
                             pItem->gmlNodeFrom->c_str());
723
0
                    return false;
724
0
                }
725
0
            }
726
0
            if (pItem->gmlNodeTo != nullptr)
727
0
            {
728
0
                sqlite3_reset(helper->hNodes);
729
0
                sqlite3_clear_bindings(helper->hNodes);
730
0
                sqlite3_bind_text(helper->hNodes, 1, pItem->gmlNodeTo->c_str(),
731
0
                                  -1, SQLITE_STATIC);
732
0
                sqlite3_bind_double(helper->hNodes, 2, pItem->xNodeTo);
733
0
                sqlite3_bind_double(helper->hNodes, 3, pItem->yNodeTo);
734
0
                if (pItem->bHasZ)
735
0
                    sqlite3_bind_double(helper->hNodes, 4, pItem->zNodeTo);
736
0
                sqlite3_bind_null(helper->hNodes, 5);
737
0
                const int rc = sqlite3_step(helper->hNodes);
738
0
                if (rc != SQLITE_OK && rc != SQLITE_DONE)
739
0
                {
740
0
                    CPLError(CE_Failure, CPLE_AppDefined,
741
0
                             "sqlite3_step() failed:\n  %s (gmlNodeTo id=%s)",
742
0
                             sqlite3_errmsg(helper->hDB),
743
0
                             pItem->gmlNodeTo->c_str());
744
0
                    return false;
745
0
                }
746
0
            }
747
0
        }
748
749
        // gml:id
750
0
        sqlite3_reset(helper->hEdges);
751
0
        sqlite3_clear_bindings(helper->hEdges);
752
0
        sqlite3_bind_text(helper->hEdges, 1, pItem->gmlId->c_str(), -1,
753
0
                          SQLITE_STATIC);
754
0
        if (pItem->bIsNodeFromHref == false && pItem->bIsNodeToHref == false)
755
0
        {
756
0
            sqlite3_bind_null(helper->hEdges, 2);
757
0
            sqlite3_bind_text(
758
0
                helper->hEdges, 3, pItem->gmlTagValue->c_str(),
759
0
                static_cast<int>(strlen(pItem->gmlTagValue->c_str())),
760
0
                SQLITE_STATIC);
761
0
        }
762
0
        else
763
0
        {
764
0
            sqlite3_bind_text(
765
0
                helper->hEdges, 2, pItem->gmlTagValue->c_str(),
766
0
                static_cast<int>(strlen(pItem->gmlTagValue->c_str())),
767
0
                SQLITE_STATIC);
768
0
            sqlite3_bind_null(helper->hEdges, 3);
769
0
        }
770
0
        if (pItem->gmlNodeFrom != nullptr)
771
0
            sqlite3_bind_text(helper->hEdges, 4, pItem->gmlNodeFrom->c_str(),
772
0
                              -1, SQLITE_STATIC);
773
0
        else
774
0
            sqlite3_bind_null(helper->hEdges, 4);
775
0
        if (pItem->bHasCoords)
776
0
        {
777
0
            sqlite3_bind_double(helper->hEdges, 5, pItem->xNodeFrom);
778
0
            sqlite3_bind_double(helper->hEdges, 6, pItem->yNodeFrom);
779
0
            if (pItem->bHasZ)
780
0
                sqlite3_bind_double(helper->hEdges, 7, pItem->zNodeFrom);
781
0
            else
782
0
                sqlite3_bind_null(helper->hEdges, 7);
783
0
        }
784
0
        else
785
0
        {
786
0
            sqlite3_bind_null(helper->hEdges, 5);
787
0
            sqlite3_bind_null(helper->hEdges, 6);
788
0
            sqlite3_bind_null(helper->hEdges, 7);
789
0
        }
790
0
        if (pItem->gmlNodeTo != nullptr)
791
0
            sqlite3_bind_text(helper->hEdges, 8, pItem->gmlNodeTo->c_str(), -1,
792
0
                              SQLITE_STATIC);
793
0
        else
794
0
            sqlite3_bind_null(helper->hEdges, 8);
795
0
        if (pItem->bHasCoords)
796
0
        {
797
0
            sqlite3_bind_double(helper->hEdges, 9, pItem->xNodeTo);
798
0
            sqlite3_bind_double(helper->hEdges, 10, pItem->yNodeTo);
799
0
            if (pItem->bHasZ)
800
0
                sqlite3_bind_double(helper->hEdges, 11, pItem->zNodeTo);
801
0
            else
802
0
                sqlite3_bind_null(helper->hEdges, 11);
803
0
        }
804
0
        else
805
0
        {
806
0
            sqlite3_bind_null(helper->hEdges, 9);
807
0
            sqlite3_bind_null(helper->hEdges, 10);
808
0
            sqlite3_bind_null(helper->hEdges, 11);
809
0
        }
810
0
        const int rc = sqlite3_step(helper->hEdges);
811
0
        if (rc != SQLITE_OK && rc != SQLITE_DONE)
812
0
        {
813
0
            CPLError(CE_Failure, CPLE_AppDefined,
814
0
                     "sqlite3_step() failed:\n  %s (edge gml:id=%s)",
815
0
                     sqlite3_errmsg(helper->hDB), pItem->gmlId->c_str());
816
0
            return false;
817
0
        }
818
0
        pItem = pItem->pNext;
819
0
    }
820
0
    return true;
821
0
}
822
823
static void gmlHugeFileReset(huge_helper *helper)
824
0
{
825
    // Resetting an empty helper struct.
826
0
    struct huge_tag *p = helper->pFirst;
827
828
    // Cleaning any previous item.
829
0
    while (p != nullptr)
830
0
    {
831
0
        struct huge_tag *pNext = p->pNext;
832
0
        if (p->gmlTagValue != nullptr)
833
0
            delete p->gmlTagValue;
834
0
        if (p->gmlId != nullptr)
835
0
            delete p->gmlId;
836
0
        if (p->gmlNodeFrom != nullptr)
837
0
            delete p->gmlNodeFrom;
838
0
        if (p->gmlNodeTo != nullptr)
839
0
            delete p->gmlNodeTo;
840
0
        delete p;
841
0
        p = pNext;
842
0
    }
843
0
    helper->pFirst = nullptr;
844
0
    helper->pLast = nullptr;
845
0
}
846
847
static void gmlHugeFileHrefReset(huge_helper *helper)
848
0
{
849
    // Resetting an empty helper struct.
850
0
    struct huge_href *p = helper->pFirstHref;
851
852
    // Cleaning any previous item.
853
0
    while (p != nullptr)
854
0
    {
855
0
        struct huge_href *pNext = p->pNext;
856
0
        if (p->gmlId != nullptr)
857
0
            delete p->gmlId;
858
0
        if (p->gmlText != nullptr)
859
0
            delete p->gmlText;
860
0
        delete p;
861
0
        p = pNext;
862
0
    }
863
0
    helper->pFirstHref = nullptr;
864
0
    helper->pLastHref = nullptr;
865
0
}
866
867
static bool gmlHugeFileHrefCheck(huge_helper *helper)
868
0
{
869
    // Testing for unresolved items.
870
0
    bool bError = false;
871
0
    struct huge_href *p = helper->pFirstHref;
872
0
    while (p != nullptr)
873
0
    {
874
0
        if (p->gmlText == nullptr)
875
0
        {
876
0
            bError = true;
877
0
            CPLError(CE_Failure, CPLE_AppDefined,
878
0
                     "Edge xlink:href\"%s\": unresolved match",
879
0
                     p->gmlId->c_str());
880
0
        }
881
0
        p = p->pNext;
882
0
    }
883
884
0
    return !bError;
885
0
}
886
887
static void gmlHugeFileRewiterReset(huge_helper *helper)
888
0
{
889
    // Resetting an empty helper struct.
890
0
    struct huge_parent *p = helper->pFirstParent;
891
892
    // Cleaning any previous item.
893
0
    while (p != nullptr)
894
0
    {
895
0
        struct huge_parent *pNext = p->pNext;
896
0
        struct huge_child *pChild = p->pFirst;
897
0
        while (pChild != nullptr)
898
0
        {
899
0
            struct huge_child *pChildNext = pChild->pNext;
900
0
            delete pChild;
901
0
            pChild = pChildNext;
902
0
        }
903
0
        delete p;
904
0
        p = pNext;
905
0
    }
906
0
    helper->pFirstParent = nullptr;
907
0
    helper->pLastParent = nullptr;
908
0
}
909
910
static struct huge_tag *gmlHugeAddToHelper(huge_helper *helper,
911
                                           CPLString *gmlId,
912
                                           CPLString *gmlFragment)
913
0
{
914
    // Adding an item into the linked list.
915
916
    // Checking against duplicates.
917
0
    struct huge_tag *pItem = helper->pFirst;
918
0
    while (pItem != nullptr)
919
0
    {
920
0
        if (EQUAL(pItem->gmlId->c_str(), gmlId->c_str()))
921
0
            return nullptr;
922
0
        pItem = pItem->pNext;
923
0
    }
924
925
0
    pItem = new struct huge_tag;
926
0
    pItem->gmlId = gmlId;
927
0
    pItem->gmlTagValue = gmlFragment;
928
0
    pItem->gmlNodeFrom = nullptr;
929
0
    pItem->gmlNodeTo = nullptr;
930
0
    pItem->bIsNodeFromHref = false;
931
0
    pItem->bIsNodeToHref = false;
932
0
    pItem->bHasCoords = false;
933
0
    pItem->bHasZ = false;
934
0
    pItem->pNext = nullptr;
935
936
    // Appending the item to the linked list.
937
0
    if (helper->pFirst == nullptr)
938
0
        helper->pFirst = pItem;
939
0
    if (helper->pLast != nullptr)
940
0
        helper->pLast->pNext = pItem;
941
0
    helper->pLast = pItem;
942
0
    return pItem;
943
0
}
944
945
static void gmlHugeAddPendingToHelper(huge_helper *helper, CPLString *gmlId,
946
                                      const CPLXMLNode *psParent,
947
                                      const CPLXMLNode *psNode,
948
                                      // bool bIsDirectedEdge,
949
                                      char cOrientation)
950
0
{
951
    // Inserting an item into the linked list.
952
953
    // Checking against duplicates.
954
0
    struct huge_href *pItem = helper->pFirstHref;
955
0
    while (pItem != nullptr)
956
0
    {
957
0
        if( EQUAL(pItem->gmlId->c_str(), gmlId->c_str()) &&
958
0
            pItem->psParent == psParent  &&
959
0
            pItem->psNode == psNode &&
960
0
            pItem->cOrientation == cOrientation /* &&
961
0
            pItem->bIsDirectedEdge == bIsDirectedEdge */ )
962
0
        {
963
0
            delete gmlId;
964
0
            return;
965
0
        }
966
0
        pItem = pItem->pNext;
967
0
    }
968
969
0
    pItem = new struct huge_href;
970
0
    pItem->gmlId = gmlId;
971
0
    pItem->gmlText = nullptr;
972
0
    pItem->psParent = psParent;
973
0
    pItem->psNode = psNode;
974
    // pItem->bIsDirectedEdge = bIsDirectedEdge;
975
0
    pItem->cOrientation = cOrientation;
976
0
    pItem->pNext = nullptr;
977
978
    // Appending the item to the linked list.
979
0
    if (helper->pFirstHref == nullptr)
980
0
        helper->pFirstHref = pItem;
981
0
    if (helper->pLastHref != nullptr)
982
0
        helper->pLastHref->pNext = pItem;
983
0
    helper->pLastHref = pItem;
984
0
}
985
986
static int gmlHugeFindGmlId(const CPLXMLNode *psNode, CPLString **gmlId)
987
0
{
988
    // Attempting to identify a gml:id value.
989
0
    *gmlId = nullptr;
990
0
    const CPLXMLNode *psChild = psNode->psChild;
991
0
    while (psChild != nullptr)
992
0
    {
993
0
        if (psChild->eType == CXT_Attribute &&
994
0
            EQUAL(psChild->pszValue, "gml:id"))
995
0
        {
996
0
            const CPLXMLNode *psIdValue = psChild->psChild;
997
0
            if (psIdValue != nullptr)
998
0
            {
999
0
                if (psIdValue->eType == CXT_Text)
1000
0
                {
1001
0
                    *gmlId = new CPLString(psIdValue->pszValue);
1002
0
                    return true;
1003
0
                }
1004
0
            }
1005
0
        }
1006
0
        psChild = psChild->psNext;
1007
0
    }
1008
0
    return false;
1009
0
}
1010
1011
static void gmlHugeFileNodeCoords(huge_helper *helper, struct huge_tag *pItem,
1012
                                  const CPLXMLNode *psNode,
1013
                                  CPL_UNUSED CPLString **nodeSrs)
1014
0
{
1015
    // This function attempts to set coordinates for <Node> items
1016
    // when required (an <Edge> is expected to be processed).
1017
1018
    // Attempting to fetch Node coordinates.
1019
0
    CPLXMLNode *psTopoCurve =
1020
0
        CPLCreateXMLNode(nullptr, CXT_Element, "TopoCurve");
1021
0
    CPLXMLNode *psDirEdge =
1022
0
        CPLCreateXMLNode(psTopoCurve, CXT_Element, "directedEdge");
1023
0
    CPLXMLNode *psEdge = CPLCloneXMLTree(psNode);
1024
0
    CPLAddXMLChild(psDirEdge, psEdge);
1025
0
    OGRGeometry *poTopoCurve =
1026
0
        GML2OGRGeometry_XMLNode(psTopoCurve, FALSE, helper->srsCache.get());
1027
0
    CPLDestroyXMLNode(psTopoCurve);
1028
0
    if (poTopoCurve != nullptr)
1029
0
    {
1030
0
        OGRGeometryCollection *poColl = poTopoCurve->toGeometryCollection();
1031
0
        const int iCount = poColl->getNumGeometries();
1032
0
        if (iCount == 1)
1033
0
        {
1034
0
            OGRGeometry *poChild = poColl->getGeometryRef(0);
1035
0
            int type = wkbFlatten(poChild->getGeometryType());
1036
0
            if (type == wkbLineString)
1037
0
            {
1038
0
                OGRLineString *poLine = poChild->toLineString();
1039
0
                const int iPoints = poLine->getNumPoints();
1040
0
                if (iPoints >= 2)
1041
0
                {
1042
0
                    pItem->bHasCoords = true;
1043
0
                    pItem->xNodeFrom = poLine->getX(0);
1044
0
                    pItem->yNodeFrom = poLine->getY(0);
1045
0
                    pItem->xNodeTo = poLine->getX(iPoints - 1);
1046
0
                    pItem->yNodeTo = poLine->getY(iPoints - 1);
1047
0
                    if (poLine->getCoordinateDimension() == 3)
1048
0
                    {
1049
0
                        pItem->zNodeFrom = poLine->getZ(0);
1050
0
                        pItem->zNodeTo = poLine->getZ(iPoints - 1);
1051
0
                        pItem->bHasZ = true;
1052
0
                    }
1053
0
                    else
1054
0
                    {
1055
0
                        pItem->bHasZ = false;
1056
0
                    }
1057
0
                }
1058
0
            }
1059
0
        }
1060
0
        delete poTopoCurve;
1061
0
    }
1062
1063
    // Searching the <directedNode> sub-tags.
1064
0
    const CPLXMLNode *psChild = psNode->psChild;
1065
0
    while (psChild != nullptr)
1066
0
    {
1067
0
        if (psChild->eType == CXT_Element &&
1068
0
            EQUAL(psChild->pszValue, "directedNode"))
1069
0
        {
1070
0
            char cOrientation = '+';
1071
0
            const char *pszGmlId = nullptr;
1072
0
            bool bIsHref = false;
1073
0
            const CPLXMLNode *psAttr = psChild->psChild;
1074
0
            while (psAttr != nullptr)
1075
0
            {
1076
0
                if (psAttr->eType == CXT_Attribute &&
1077
0
                    EQUAL(psAttr->pszValue, "xlink:href"))
1078
0
                {
1079
0
                    const CPLXMLNode *psHref = psAttr->psChild;
1080
0
                    if (psHref != nullptr)
1081
0
                    {
1082
0
                        if (psHref->eType == CXT_Text)
1083
0
                        {
1084
0
                            pszGmlId = psHref->pszValue;
1085
0
                            bIsHref = true;
1086
0
                        }
1087
0
                    }
1088
0
                }
1089
0
                if (psAttr->eType == CXT_Attribute &&
1090
0
                    EQUAL(psAttr->pszValue, "orientation"))
1091
0
                {
1092
0
                    const CPLXMLNode *psOrientation = psAttr->psChild;
1093
0
                    if (psOrientation != nullptr)
1094
0
                    {
1095
0
                        if (psOrientation->eType == CXT_Text)
1096
0
                        {
1097
0
                            cOrientation = *(psOrientation->pszValue);
1098
0
                        }
1099
0
                    }
1100
0
                }
1101
0
                if (psAttr->eType == CXT_Element &&
1102
0
                    EQUAL(psAttr->pszValue, "Node"))
1103
0
                {
1104
0
                    const CPLXMLNode *psId = psAttr->psChild;
1105
0
                    while (psId != nullptr)
1106
0
                    {
1107
0
                        if (psId->eType == CXT_Attribute &&
1108
0
                            EQUAL(psId->pszValue, "gml:id"))
1109
0
                        {
1110
0
                            const CPLXMLNode *psIdGml = psId->psChild;
1111
0
                            if (psIdGml != nullptr)
1112
0
                            {
1113
0
                                if (psIdGml->eType == CXT_Text)
1114
0
                                {
1115
0
                                    pszGmlId = psIdGml->pszValue;
1116
0
                                    bIsHref = false;
1117
0
                                }
1118
0
                            }
1119
0
                        }
1120
0
                        psId = psId->psNext;
1121
0
                    }
1122
0
                }
1123
0
                psAttr = psAttr->psNext;
1124
0
            }
1125
0
            if (pszGmlId != nullptr)
1126
0
            {
1127
0
                CPLString *posNode = nullptr;
1128
0
                if (bIsHref)
1129
0
                {
1130
0
                    if (pszGmlId[0] != '#')
1131
0
                    {
1132
0
                        CPLError(CE_Warning, CPLE_NotSupported,
1133
0
                                 "Only values of xlink:href element starting "
1134
0
                                 "with '#' are supported, "
1135
0
                                 "so %s will not be properly recognized",
1136
0
                                 pszGmlId);
1137
0
                    }
1138
0
                    posNode = new CPLString(pszGmlId + 1);
1139
0
                }
1140
0
                else
1141
0
                {
1142
0
                    posNode = new CPLString(pszGmlId);
1143
0
                }
1144
0
                if (cOrientation == '-')
1145
0
                {
1146
0
                    pItem->gmlNodeFrom = posNode;
1147
0
                    pItem->bIsNodeFromHref = bIsHref;
1148
0
                }
1149
0
                else
1150
0
                {
1151
0
                    pItem->gmlNodeTo = posNode;
1152
0
                    pItem->bIsNodeToHref = bIsHref;
1153
0
                }
1154
                // pszGmlId = NULL;
1155
                // *bIsHref = false;
1156
                // cOrientation = '+';
1157
0
            }
1158
0
        }
1159
0
        psChild = psChild->psNext;
1160
0
    }
1161
0
}
1162
1163
static void gmlHugeFileCheckXrefs(huge_helper *helper, const CPLXMLNode *psNode)
1164
0
{
1165
    // Identifying <Edge> GML nodes.
1166
0
    if (psNode->eType == CXT_Element)
1167
0
    {
1168
0
        if (EQUAL(psNode->pszValue, "Edge"))
1169
0
        {
1170
0
            CPLString *gmlId = nullptr;
1171
0
            if (gmlHugeFindGmlId(psNode, &gmlId))
1172
0
            {
1173
0
                char *gmlText = CPLSerializeXMLTree(psNode);
1174
0
                CPLString *gmlValue = new CPLString(gmlText);
1175
0
                CPLFree(gmlText);
1176
0
                struct huge_tag *pItem =
1177
0
                    gmlHugeAddToHelper(helper, gmlId, gmlValue);
1178
0
                if (pItem != nullptr)
1179
0
                {
1180
0
                    gmlHugeFileNodeCoords(helper, pItem, psNode,
1181
0
                                          &(helper->nodeSrs));
1182
0
                }
1183
0
                else
1184
0
                {
1185
0
                    delete gmlId;
1186
0
                    delete gmlValue;
1187
0
                }
1188
0
            }
1189
0
        }
1190
0
    }
1191
1192
    // Recursively scanning each Child GML node.
1193
0
    const CPLXMLNode *psChild = psNode->psChild;
1194
0
    while (psChild != nullptr)
1195
0
    {
1196
0
        if (psChild->eType == CXT_Element)
1197
0
        {
1198
0
            if (EQUAL(psChild->pszValue, "Edge") ||
1199
0
                EQUAL(psChild->pszValue, "directedEdge"))
1200
0
            {
1201
0
                gmlHugeFileCheckXrefs(helper, psChild);
1202
0
            }
1203
0
            if (EQUAL(psChild->pszValue, "directedFace"))
1204
0
            {
1205
0
                const CPLXMLNode *psFace = psChild->psChild;
1206
0
                if (psFace != nullptr)
1207
0
                {
1208
0
                    if (psFace->eType == CXT_Element &&
1209
0
                        EQUAL(psFace->pszValue, "Face"))
1210
0
                    {
1211
0
                        const CPLXMLNode *psDirEdge = psFace->psChild;
1212
0
                        while (psDirEdge != nullptr)
1213
0
                        {
1214
0
                            const CPLXMLNode *psEdge = psDirEdge->psChild;
1215
0
                            while (psEdge != nullptr)
1216
0
                            {
1217
0
                                if (psEdge->eType == CXT_Element &&
1218
0
                                    EQUAL(psEdge->pszValue, "Edge"))
1219
0
                                    gmlHugeFileCheckXrefs(helper, psEdge);
1220
0
                                psEdge = psEdge->psNext;
1221
0
                            }
1222
0
                            psDirEdge = psDirEdge->psNext;
1223
0
                        }
1224
0
                    }
1225
0
                }
1226
0
            }
1227
0
        }
1228
0
        psChild = psChild->psNext;
1229
0
    }
1230
1231
    // Recursively scanning each GML of the same level.
1232
0
    const CPLXMLNode *psNext = psNode->psNext;
1233
0
    while (psNext != nullptr)
1234
0
    {
1235
0
        if (psNext->eType == CXT_Element)
1236
0
        {
1237
0
            if (EQUAL(psNext->pszValue, "Edge") ||
1238
0
                EQUAL(psNext->pszValue, "directedEdge"))
1239
0
            {
1240
0
                gmlHugeFileCheckXrefs(helper, psNext);
1241
0
            }
1242
0
        }
1243
0
        psNext = psNext->psNext;
1244
0
    }
1245
0
}
1246
1247
static void gmlHugeFileCleanUp(huge_helper *helper)
1248
0
{
1249
    // Cleaning up any SQLite handle.
1250
0
    if (helper->hNodes != nullptr)
1251
0
        sqlite3_finalize(helper->hNodes);
1252
0
    if (helper->hEdges != nullptr)
1253
0
        sqlite3_finalize(helper->hEdges);
1254
0
    if (helper->hDB != nullptr)
1255
0
        sqlite3_close(helper->hDB);
1256
0
    if (helper->nodeSrs != nullptr)
1257
0
        delete helper->nodeSrs;
1258
0
}
1259
1260
static void gmlHugeFileCheckPendingHrefs(huge_helper *helper,
1261
                                         const CPLXMLNode *psParent,
1262
                                         const CPLXMLNode *psNode)
1263
0
{
1264
    // Identifying any xlink:href to be replaced.
1265
0
    if (psNode->eType == CXT_Element)
1266
0
    {
1267
0
        if (EQUAL(psNode->pszValue, "directedEdge"))
1268
0
        {
1269
0
            char cOrientation = '+';
1270
0
            CPLXMLNode *psAttr = psNode->psChild;
1271
0
            while (psAttr != nullptr)
1272
0
            {
1273
0
                if (psAttr->eType == CXT_Attribute &&
1274
0
                    EQUAL(psAttr->pszValue, "orientation"))
1275
0
                {
1276
0
                    const CPLXMLNode *psOrientation = psAttr->psChild;
1277
0
                    if (psOrientation != nullptr)
1278
0
                    {
1279
0
                        if (psOrientation->eType == CXT_Text)
1280
0
                            cOrientation = *(psOrientation->pszValue);
1281
0
                    }
1282
0
                }
1283
0
                psAttr = psAttr->psNext;
1284
0
            }
1285
0
            psAttr = psNode->psChild;
1286
0
            while (psAttr != nullptr)
1287
0
            {
1288
0
                if (psAttr->eType == CXT_Attribute &&
1289
0
                    EQUAL(psAttr->pszValue, "xlink:href"))
1290
0
                {
1291
0
                    const CPLXMLNode *pszHref = psAttr->psChild;
1292
0
                    if (pszHref != nullptr)
1293
0
                    {
1294
0
                        if (pszHref->eType == CXT_Text)
1295
0
                        {
1296
0
                            if (pszHref->pszValue[0] != '#')
1297
0
                            {
1298
0
                                CPLError(
1299
0
                                    CE_Warning, CPLE_NotSupported,
1300
0
                                    "Only values of xlink:href element "
1301
0
                                    "starting with '#' are supported, "
1302
0
                                    "so %s will not be properly recognized",
1303
0
                                    pszHref->pszValue);
1304
0
                            }
1305
0
                            CPLString *gmlId =
1306
0
                                new CPLString(pszHref->pszValue + 1);
1307
0
                            gmlHugeAddPendingToHelper(
1308
0
                                helper, gmlId, psParent, psNode,
1309
                                // /*bDirectedEdge=*/ true,
1310
0
                                cOrientation);
1311
0
                        }
1312
0
                    }
1313
0
                }
1314
0
                psAttr = psAttr->psNext;
1315
0
            }
1316
0
        }
1317
0
    }
1318
1319
    // Recursively scanning each Child GML node.
1320
0
    const CPLXMLNode *psChild = psNode->psChild;
1321
0
    while (psChild != nullptr)
1322
0
    {
1323
0
        if (psChild->eType == CXT_Element)
1324
0
        {
1325
0
            if (EQUAL(psChild->pszValue, "directedEdge") ||
1326
0
                EQUAL(psChild->pszValue, "directedFace") ||
1327
0
                EQUAL(psChild->pszValue, "Face"))
1328
0
            {
1329
0
                gmlHugeFileCheckPendingHrefs(helper, psNode, psChild);
1330
0
            }
1331
0
        }
1332
0
        psChild = psChild->psNext;
1333
0
    }
1334
1335
    // Recursively scanning each GML of the same level.
1336
0
    const CPLXMLNode *psNext = psNode->psNext;
1337
0
    while (psNext != nullptr)
1338
0
    {
1339
0
        if (psNext->eType == CXT_Element)
1340
0
        {
1341
0
            if (EQUAL(psNext->pszValue, "Face"))
1342
0
            {
1343
0
                gmlHugeFileCheckPendingHrefs(helper, psParent, psNext);
1344
0
            }
1345
0
        }
1346
0
        psNext = psNext->psNext;
1347
0
    }
1348
0
}
1349
1350
static void gmlHugeSetHrefGmlText(huge_helper *helper, const char *pszGmlId,
1351
                                  const char *pszGmlText)
1352
0
{
1353
    // Setting the GML text for the corresponding gml:id.
1354
0
    struct huge_href *pItem = helper->pFirstHref;
1355
0
    while (pItem != nullptr)
1356
0
    {
1357
0
        if (EQUAL(pItem->gmlId->c_str(), pszGmlId))
1358
0
        {
1359
0
            if (pItem->gmlText != nullptr)
1360
0
                delete pItem->gmlText;
1361
0
            pItem->gmlText = new CPLString(pszGmlText);
1362
0
            return;
1363
0
        }
1364
0
        pItem = pItem->pNext;
1365
0
    }
1366
0
}
1367
1368
static struct huge_parent *gmlHugeFindParent(huge_helper *helper,
1369
                                             CPLXMLNode *psParent)
1370
0
{
1371
    // Inserting a GML Node (parent) to be rewritten.
1372
0
    struct huge_parent *pItem = helper->pFirstParent;
1373
1374
    // Checking if already exists.
1375
0
    while (pItem != nullptr)
1376
0
    {
1377
0
        if (pItem->psParent == psParent)
1378
0
            return pItem;
1379
0
        pItem = pItem->pNext;
1380
0
    }
1381
1382
    // Creating a new Parent Node.
1383
0
    pItem = new struct huge_parent;
1384
0
    pItem->psParent = psParent;
1385
0
    pItem->pFirst = nullptr;
1386
0
    pItem->pLast = nullptr;
1387
0
    pItem->pNext = nullptr;
1388
0
    if (helper->pFirstParent == nullptr)
1389
0
        helper->pFirstParent = pItem;
1390
0
    if (helper->pLastParent != nullptr)
1391
0
        helper->pLastParent->pNext = pItem;
1392
0
    helper->pLastParent = pItem;
1393
1394
    // Inserting any Child node into the Parent.
1395
0
    CPLXMLNode *psChild = psParent->psChild;
1396
0
    while (psChild != nullptr)
1397
0
    {
1398
0
        struct huge_child *pChildItem = new struct huge_child;
1399
0
        pChildItem->psChild = psChild;
1400
0
        pChildItem->pItem = nullptr;
1401
0
        pChildItem->pNext = nullptr;
1402
0
        if (pItem->pFirst == nullptr)
1403
0
            pItem->pFirst = pChildItem;
1404
0
        if (pItem->pLast != nullptr)
1405
0
            pItem->pLast->pNext = pChildItem;
1406
0
        pItem->pLast = pChildItem;
1407
0
        psChild = psChild->psNext;
1408
0
    }
1409
0
    return pItem;
1410
0
}
1411
1412
static bool gmlHugeSetChild(struct huge_parent *pParent,
1413
                            struct huge_href *pItem)
1414
0
{
1415
    // Setting a Child Node to be rewritten.
1416
0
    struct huge_child *pChild = pParent->pFirst;
1417
0
    while (pChild != nullptr)
1418
0
    {
1419
0
        if (pChild->psChild == pItem->psNode)
1420
0
        {
1421
0
            pChild->pItem = pItem;
1422
0
            return true;
1423
0
        }
1424
0
        pChild = pChild->pNext;
1425
0
    }
1426
0
    return false;
1427
0
}
1428
1429
static bool gmlHugeResolveEdges(CPL_UNUSED huge_helper *helper,
1430
                                CPL_UNUSED CPLXMLNode *psNode, sqlite3 *hDB)
1431
0
{
1432
    // Resolving GML <Edge> xlink:href.
1433
0
    CPLString osCommand;
1434
0
    bool bIsComma = false;
1435
0
    bool bError = false;
1436
1437
    // query cursor [Edges] */
1438
0
    osCommand = "SELECT gml_id, gml_resolved "
1439
0
                "FROM gml_edges "
1440
0
                "WHERE gml_id IN (";
1441
0
    struct huge_href *pItem = helper->pFirstHref;
1442
0
    while (pItem != nullptr)
1443
0
    {
1444
0
        if (bIsComma)
1445
0
            osCommand += ", ";
1446
0
        else
1447
0
            bIsComma = true;
1448
0
        osCommand += "'";
1449
0
        osCommand += pItem->gmlId->c_str();
1450
0
        osCommand += "'";
1451
0
        pItem = pItem->pNext;
1452
0
    }
1453
0
    osCommand += ")";
1454
0
    sqlite3_stmt *hStmtEdges = nullptr;
1455
0
    {
1456
0
        const int rc = sqlite3_prepare_v2(hDB, osCommand.c_str(), -1,
1457
0
                                          &hStmtEdges, nullptr);
1458
0
        if (rc != SQLITE_OK)
1459
0
        {
1460
0
            CPLError(CE_Failure, CPLE_AppDefined,
1461
0
                     "Unable to create QUERY stmt for EDGES");
1462
0
            return false;
1463
0
        }
1464
0
    }
1465
0
    while (true)
1466
0
    {
1467
0
        const int rc = sqlite3_step(hStmtEdges);
1468
0
        if (rc == SQLITE_DONE)
1469
0
            break;
1470
0
        if (rc == SQLITE_ROW)
1471
0
        {
1472
0
            const char *pszGmlId = reinterpret_cast<const char *>(
1473
0
                sqlite3_column_text(hStmtEdges, 0));
1474
0
            if (sqlite3_column_type(hStmtEdges, 1) != SQLITE_NULL)
1475
0
            {
1476
0
                const char *pszGmlText = reinterpret_cast<const char *>(
1477
0
                    sqlite3_column_text(hStmtEdges, 1));
1478
0
                gmlHugeSetHrefGmlText(helper, pszGmlId, pszGmlText);
1479
0
            }
1480
0
        }
1481
0
        else
1482
0
        {
1483
0
            CPLError(CE_Failure, CPLE_AppDefined,
1484
0
                     "Edge xlink:href QUERY: sqlite3_step(%s)",
1485
0
                     sqlite3_errmsg(hDB));
1486
0
            bError = true;
1487
0
            break;
1488
0
        }
1489
0
    }
1490
0
    sqlite3_finalize(hStmtEdges);
1491
0
    if (bError)
1492
0
        return false;
1493
1494
    // Identifying any GML node to be rewritten.
1495
0
    pItem = helper->pFirstHref;
1496
0
    struct huge_parent *pParent = nullptr;
1497
0
    while (pItem != nullptr)
1498
0
    {
1499
0
        if (pItem->gmlText == nullptr || pItem->psParent == nullptr ||
1500
0
            pItem->psNode == nullptr)
1501
0
        {
1502
0
            bError = true;
1503
0
            break;
1504
0
        }
1505
0
        pParent = gmlHugeFindParent(helper,
1506
0
                                    const_cast<CPLXMLNode *>(pItem->psParent));
1507
0
        if (gmlHugeSetChild(pParent, pItem) == false)
1508
0
        {
1509
0
            bError = true;
1510
0
            break;
1511
0
        }
1512
0
        pItem = pItem->pNext;
1513
0
    }
1514
1515
0
    if (bError == false)
1516
0
    {
1517
        // Rewriting GML nodes.
1518
0
        pParent = helper->pFirstParent;
1519
0
        while (pParent != nullptr)
1520
0
        {
1521
1522
            // Removing any Child node from the Parent.
1523
0
            struct huge_child *pChild = pParent->pFirst;
1524
0
            while (pChild != nullptr)
1525
0
            {
1526
0
                CPLRemoveXMLChild(pParent->psParent, pChild->psChild);
1527
1528
                // Destroying any Child Node to be rewritten.
1529
0
                if (pChild->pItem != nullptr)
1530
0
                    CPLDestroyXMLNode(pChild->psChild);
1531
0
                pChild = pChild->pNext;
1532
0
            }
1533
1534
            // Rewriting the Parent Node.
1535
0
            pChild = pParent->pFirst;
1536
0
            while (pChild != nullptr)
1537
0
            {
1538
0
                if (pChild->pItem == nullptr)
1539
0
                {
1540
                    // Reinserting any untouched Child Node.
1541
0
                    CPLAddXMLChild(pParent->psParent, pChild->psChild);
1542
0
                }
1543
0
                else
1544
0
                {
1545
                    // Rewriting a Child Node.
1546
0
                    CPLXMLNode *psNewNode =
1547
0
                        CPLCreateXMLNode(nullptr, CXT_Element, "directedEdge");
1548
0
                    if (pChild->pItem->cOrientation == '-')
1549
0
                    {
1550
0
                        CPLXMLNode *psOrientationNode = CPLCreateXMLNode(
1551
0
                            psNewNode, CXT_Attribute, "orientation");
1552
0
                        CPLCreateXMLNode(psOrientationNode, CXT_Text, "-");
1553
0
                    }
1554
0
                    CPLXMLNode *psEdge =
1555
0
                        CPLParseXMLString(pChild->pItem->gmlText->c_str());
1556
0
                    if (psEdge != nullptr)
1557
0
                        CPLAddXMLChild(psNewNode, psEdge);
1558
0
                    CPLAddXMLChild(pParent->psParent, psNewNode);
1559
0
                }
1560
0
                pChild = pChild->pNext;
1561
0
            }
1562
0
            pParent = pParent->pNext;
1563
0
        }
1564
0
    }
1565
1566
    // Resetting the Rewrite Helper to an empty state.
1567
0
    gmlHugeFileRewiterReset(helper);
1568
1569
0
    return !bError;
1570
0
}
1571
1572
static bool gmlHugeFileWriteResolved(huge_helper *helper,
1573
                                     const char *pszOutputFilename,
1574
                                     GMLReader *pReader,
1575
                                     GMLAppSchemaType eAppSchemaType,
1576
                                     int *m_nHasSequentialLayers)
1577
0
{
1578
    // Open the resolved GML file for writing.
1579
0
    VSILFILE *fp = VSIFOpenL(pszOutputFilename, "w");
1580
0
    if (fp == nullptr)
1581
0
    {
1582
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %.500s to write.",
1583
0
                 pszOutputFilename);
1584
0
        return false;
1585
0
    }
1586
1587
0
    sqlite3 *hDB = helper->hDB;
1588
1589
    // Query cursor [Nodes].
1590
0
    const char *osCommand = "SELECT gml_id, x, y, z FROM nodes";
1591
0
    sqlite3_stmt *hStmtNodes = nullptr;
1592
0
    const int rc1 =
1593
0
        sqlite3_prepare_v2(hDB, osCommand, -1, &hStmtNodes, nullptr);
1594
0
    if (rc1 != SQLITE_OK)
1595
0
    {
1596
0
        CPLError(CE_Failure, CPLE_AppDefined,
1597
0
                 "Unable to create QUERY stmt for NODES");
1598
0
        VSIFCloseL(fp);
1599
0
        return false;
1600
0
    }
1601
1602
0
    const char *pszTopElement = "ResolvedTopoFeatureMembers";
1603
    // For some specific application schema, GMLHandler has specific behavior,
1604
    // so re-use the root XML element it recognizes.
1605
0
    if (eAppSchemaType == APPSCHEMA_AIXM)
1606
0
        pszTopElement = "AIXMBasicMessage";
1607
0
    else if (eAppSchemaType == APPSCHEMA_CITYGML)
1608
0
        pszTopElement = "CityModel";
1609
1610
0
    VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1611
0
    VSIFPrintfL(fp,
1612
0
                "<%s  "
1613
0
                "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
1614
0
                "xmlns:gml=\"http://www.opengis.net/gml\">\n",
1615
0
                pszTopElement);
1616
0
    VSIFPrintfL(fp, "  <ResolvedTopoFeatureMembers>\n");
1617
1618
0
    int iOutCount = 0;
1619
1620
    // Exporting Nodes.
1621
0
    GFSTemplateList *pCC = new GFSTemplateList();
1622
0
    while (true)
1623
0
    {
1624
0
        const int rc = sqlite3_step(hStmtNodes);
1625
0
        if (rc == SQLITE_DONE)
1626
0
            break;
1627
1628
0
        if (rc == SQLITE_ROW)
1629
0
        {
1630
0
            bool bHasZ = false;
1631
0
            const char *pszGmlId = reinterpret_cast<const char *>(
1632
0
                sqlite3_column_text(hStmtNodes, 0));
1633
0
            const double x = sqlite3_column_double(hStmtNodes, 1);
1634
0
            const double y = sqlite3_column_double(hStmtNodes, 2);
1635
0
            double z = 0.0;
1636
0
            if (sqlite3_column_type(hStmtNodes, 3) == SQLITE_FLOAT)
1637
0
            {
1638
0
                z = sqlite3_column_double(hStmtNodes, 3);
1639
0
                bHasZ = true;
1640
0
            }
1641
1642
            // Inserting a node into the resolved GML file.
1643
0
            pCC->Update("ResolvedNodes", true);
1644
0
            VSIFPrintfL(fp, "    <ResolvedNodes>\n");
1645
0
            char *pszEscaped = CPLEscapeString(pszGmlId, -1, CPLES_XML);
1646
0
            VSIFPrintfL(fp, "      <NodeGmlId>%s</NodeGmlId>\n", pszEscaped);
1647
0
            CPLFree(pszEscaped);
1648
0
            VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
1649
0
            if (helper->nodeSrs == nullptr)
1650
0
            {
1651
0
                VSIFPrintfL(fp, "        <gml:Point srsDimension=\"%d\">",
1652
0
                            bHasZ ? 3 : 2);
1653
0
            }
1654
0
            else
1655
0
            {
1656
0
                pszEscaped =
1657
0
                    CPLEscapeString(helper->nodeSrs->c_str(), -1, CPLES_XML);
1658
0
                VSIFPrintfL(fp,
1659
0
                            "        <gml:Point srsDimension=\"%d\""
1660
0
                            " srsName=\"%s\">",
1661
0
                            bHasZ ? 3 : 2, pszEscaped);
1662
0
                CPLFree(pszEscaped);
1663
0
            }
1664
0
            if (bHasZ)
1665
0
                VSIFPrintfL(fp,
1666
0
                            "<gml:pos>%1.8f %1.8f %1.8f</gml:pos>"
1667
0
                            "</gml:Point>\n",
1668
0
                            x, y, z);
1669
0
            else
1670
0
                VSIFPrintfL(fp,
1671
0
                            "<gml:pos>%1.8f %1.8f</gml:pos>"
1672
0
                            "</gml:Point>\n",
1673
0
                            x, y);
1674
0
            VSIFPrintfL(fp, "      </ResolvedGeometry> \n");
1675
0
            VSIFPrintfL(fp, "    </ResolvedNodes>\n");
1676
0
            iOutCount++;
1677
0
        }
1678
0
        else
1679
0
        {
1680
0
            CPLError(CE_Failure, CPLE_AppDefined,
1681
0
                     "ResolvedNodes QUERY: sqlite3_step(%s)",
1682
0
                     sqlite3_errmsg(hDB));
1683
0
            sqlite3_finalize(hStmtNodes);
1684
0
            delete pCC;
1685
0
            return false;
1686
0
        }
1687
0
    }
1688
0
    sqlite3_finalize(hStmtNodes);
1689
1690
    // Processing GML features.
1691
0
    GMLFeature *poFeature = nullptr;
1692
0
    bool bError = false;
1693
1694
0
    while ((poFeature = pReader->NextFeature()) != nullptr)
1695
0
    {
1696
0
        GMLFeatureClass *poClass = poFeature->GetClass();
1697
0
        const CPLXMLNode *const *papsGeomList = poFeature->GetGeometryList();
1698
0
        const int iPropCount = poClass->GetPropertyCount();
1699
1700
0
        bool b_has_geom = false;
1701
0
        VSIFPrintfL(fp, "    <%s", poClass->GetElementName());
1702
0
        const char *pszGmlId = poFeature->GetFID();
1703
0
        if (pszGmlId)
1704
0
        {
1705
0
            char *gmlText = CPLEscapeString(pszGmlId, -1, CPLES_XML);
1706
0
            VSIFPrintfL(fp, " gml:id=\"%s\"", gmlText);
1707
0
            CPLFree(gmlText);
1708
0
        }
1709
0
        VSIFPrintfL(fp, ">\n");
1710
1711
0
        for (int iProp = 0; iProp < iPropCount; iProp++)
1712
0
        {
1713
0
            GMLPropertyDefn *poPropDefn = poClass->GetProperty(iProp);
1714
0
            const char *pszPropName = poPropDefn->GetName();
1715
0
            const GMLProperty *poProp = poFeature->GetProperty(iProp);
1716
1717
0
            if (poProp != nullptr)
1718
0
            {
1719
0
                for (int iSub = 0; iSub < poProp->nSubProperties; iSub++)
1720
0
                {
1721
0
                    char *gmlText = CPLEscapeString(
1722
0
                        poProp->papszSubProperties[iSub], -1, CPLES_XML);
1723
0
                    if (strchr(pszPropName, '|'))
1724
0
                    {
1725
0
                        const CPLStringList aosPropNameComps(
1726
0
                            CSLTokenizeString2(pszPropName, "|", 0));
1727
0
                        VSIFPrintfL(fp, "      ");
1728
0
                        for (int i = 0; i < aosPropNameComps.size(); ++i)
1729
0
                        {
1730
0
                            VSIFPrintfL(fp, "<%s>", aosPropNameComps[i]);
1731
0
                        }
1732
0
                        VSIFPrintfL(fp, "%s", gmlText);
1733
0
                        for (int i = aosPropNameComps.size() - 1; i >= 0; --i)
1734
0
                        {
1735
0
                            VSIFPrintfL(fp, "</%s>", aosPropNameComps[i]);
1736
0
                        }
1737
0
                        VSIFPrintfL(fp, "\n");
1738
0
                    }
1739
0
                    else
1740
0
                    {
1741
0
                        VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszPropName,
1742
0
                                    gmlText, pszPropName);
1743
0
                    }
1744
0
                    CPLFree(gmlText);
1745
0
                }
1746
0
            }
1747
0
        }
1748
1749
0
        if (papsGeomList != nullptr)
1750
0
        {
1751
0
            int i = 0;
1752
0
            const CPLXMLNode *psNode = papsGeomList[i];
1753
0
            while (psNode != nullptr)
1754
0
            {
1755
0
                char *pszResolved = nullptr;
1756
0
                bool bNotToBeResolved = false;
1757
0
                if (psNode->eType != CXT_Element)
1758
0
                {
1759
0
                    bNotToBeResolved = true;
1760
0
                }
1761
0
                else
1762
0
                {
1763
0
                    bNotToBeResolved =
1764
0
                        !(EQUAL(psNode->pszValue, "TopoCurve") ||
1765
0
                          EQUAL(psNode->pszValue, "TopoSurface"));
1766
0
                }
1767
0
                if (bNotToBeResolved)
1768
0
                {
1769
0
                    VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
1770
0
                    pszResolved = CPLSerializeXMLTree(psNode);
1771
0
                    VSIFPrintfL(fp, "        %s\n", pszResolved);
1772
0
                    CPLFree(pszResolved);
1773
0
                    VSIFPrintfL(fp, "      </ResolvedGeometry>\n");
1774
0
                    b_has_geom = true;
1775
0
                }
1776
0
                else
1777
0
                {
1778
0
                    gmlHugeFileCheckPendingHrefs(helper, psNode, psNode);
1779
0
                    if (helper->pFirstHref == nullptr)
1780
0
                    {
1781
0
                        VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
1782
0
                        pszResolved = CPLSerializeXMLTree(psNode);
1783
0
                        VSIFPrintfL(fp, "        %s\n", pszResolved);
1784
0
                        CPLFree(pszResolved);
1785
0
                        VSIFPrintfL(fp, "      </ResolvedGeometry>\n");
1786
0
                        b_has_geom = true;
1787
0
                    }
1788
0
                    else
1789
0
                    {
1790
0
                        if (gmlHugeResolveEdges(
1791
0
                                helper, const_cast<CPLXMLNode *>(psNode),
1792
0
                                hDB) == false)
1793
0
                            bError = true;
1794
0
                        if (gmlHugeFileHrefCheck(helper) == false)
1795
0
                            bError = true;
1796
0
                        VSIFPrintfL(fp, "      <ResolvedGeometry> \n");
1797
0
                        pszResolved = CPLSerializeXMLTree(psNode);
1798
0
                        VSIFPrintfL(fp, "        %s\n", pszResolved);
1799
0
                        CPLFree(pszResolved);
1800
0
                        VSIFPrintfL(fp, "      </ResolvedGeometry>\n");
1801
0
                        b_has_geom = true;
1802
0
                        gmlHugeFileHrefReset(helper);
1803
0
                    }
1804
0
                }
1805
0
                i++;
1806
0
                psNode = papsGeomList[i];
1807
0
            }
1808
0
        }
1809
0
        pCC->Update(poClass->GetElementName(), b_has_geom);
1810
1811
0
        VSIFPrintfL(fp, "    </%s>\n", poClass->GetElementName());
1812
1813
0
        delete poFeature;
1814
0
        iOutCount++;
1815
0
    }
1816
1817
0
    VSIFPrintfL(fp, "  </ResolvedTopoFeatureMembers>\n");
1818
0
    VSIFPrintfL(fp, "</%s>\n", pszTopElement);
1819
1820
0
    VSIFCloseL(fp);
1821
1822
0
    gmlUpdateFeatureClasses(pCC, pReader, m_nHasSequentialLayers);
1823
0
    if (*m_nHasSequentialLayers)
1824
0
        pReader->ReArrangeTemplateClasses(pCC);
1825
0
    delete pCC;
1826
1827
0
    return !(bError || iOutCount == 0);
1828
0
}
1829
1830
/**************************************************************/
1831
/*                                                            */
1832
/* private member(s):                                         */
1833
/* any other function is implemented as "internal" static,    */
1834
/* so to make all the SQLite own stuff nicely "invisible"     */
1835
/*                                                            */
1836
/**************************************************************/
1837
1838
bool GMLReader::ParseXMLHugeFile(const char *pszOutputFilename,
1839
                                 const bool bSqliteIsTempFile,
1840
                                 const int iSqliteCacheMB)
1841
1842
0
{
1843
    /* -------------------------------------------------------------------- */
1844
    /*      Creating/Opening the SQLite DB file                             */
1845
    /* -------------------------------------------------------------------- */
1846
0
    const std::string osSQLiteFilename =
1847
0
        CPLResetExtensionSafe(m_pszFilename, "sqlite");
1848
0
    const char *pszSQLiteFilename = osSQLiteFilename.c_str();
1849
1850
0
    VSIStatBufL statBufL;
1851
0
    if (VSIStatExL(pszSQLiteFilename, &statBufL, VSI_STAT_EXISTS_FLAG) == 0)
1852
0
    {
1853
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1854
0
                 "sqlite3_open(%s) failed: DB-file already exists",
1855
0
                 pszSQLiteFilename);
1856
0
        return false;
1857
0
    }
1858
1859
0
    sqlite3 *hDB = nullptr;
1860
0
    {
1861
0
        const int rc = sqlite3_open(pszSQLiteFilename, &hDB);
1862
0
        if (rc != SQLITE_OK)
1863
0
        {
1864
0
            CPLError(CE_Failure, CPLE_OpenFailed, "sqlite3_open(%s) failed: %s",
1865
0
                     pszSQLiteFilename, sqlite3_errmsg(hDB));
1866
0
            sqlite3_close(hDB);
1867
0
            return false;
1868
0
        }
1869
0
    }
1870
1871
0
    huge_helper helper;
1872
0
    helper.hDB = hDB;
1873
1874
0
    char *pszErrMsg = nullptr;
1875
1876
    // Setting SQLite for max speed; this is intrinsically unsafe.
1877
    // The DB file could be potentially damaged.
1878
    // But, this is a temporary file, so there is no real risk.
1879
0
    {
1880
0
        const int rc = sqlite3_exec(hDB, "PRAGMA synchronous = OFF", nullptr,
1881
0
                                    nullptr, &pszErrMsg);
1882
0
        if (rc != SQLITE_OK)
1883
0
        {
1884
0
            CPLError(CE_Failure, CPLE_AppDefined,
1885
0
                     "Unable to set PRAGMA synchronous = OFF: %s", pszErrMsg);
1886
0
            sqlite3_free(pszErrMsg);
1887
0
        }
1888
0
    }
1889
0
    {
1890
0
        const int rc = sqlite3_exec(hDB, "PRAGMA journal_mode = OFF", nullptr,
1891
0
                                    nullptr, &pszErrMsg);
1892
0
        if (rc != SQLITE_OK)
1893
0
        {
1894
0
            CPLError(CE_Failure, CPLE_AppDefined,
1895
0
                     "Unable to set PRAGMA journal_mode = OFF: %s", pszErrMsg);
1896
0
            sqlite3_free(pszErrMsg);
1897
0
        }
1898
0
    }
1899
0
    {
1900
0
        const int rc = sqlite3_exec(hDB, "PRAGMA locking_mode = EXCLUSIVE",
1901
0
                                    nullptr, nullptr, &pszErrMsg);
1902
0
        if (rc != SQLITE_OK)
1903
0
        {
1904
0
            CPLError(CE_Failure, CPLE_AppDefined,
1905
0
                     "Unable to set PRAGMA locking_mode = EXCLUSIVE: %s",
1906
0
                     pszErrMsg);
1907
0
            sqlite3_free(pszErrMsg);
1908
0
        }
1909
0
    }
1910
1911
    // Setting the SQLite cache.
1912
0
    if (iSqliteCacheMB > 0)
1913
0
    {
1914
        // Refuse to allocate more than 1GB.
1915
0
        const int cache_size = std::min(iSqliteCacheMB * 1024, 1024 * 1024);
1916
1917
0
        char sqlPragma[64] = {};
1918
0
        snprintf(sqlPragma, sizeof(sqlPragma), "PRAGMA cache_size = %d",
1919
0
                 cache_size);
1920
0
        const int rc =
1921
0
            sqlite3_exec(hDB, sqlPragma, nullptr, nullptr, &pszErrMsg);
1922
0
        if (rc != SQLITE_OK)
1923
0
        {
1924
0
            CPLError(CE_Failure, CPLE_AppDefined, "Unable to set %s: %s",
1925
0
                     sqlPragma, pszErrMsg);
1926
0
            sqlite3_free(pszErrMsg);
1927
0
        }
1928
0
    }
1929
1930
0
    if (!SetupParser())
1931
0
    {
1932
0
        gmlHugeFileCleanUp(&helper);
1933
0
        return false;
1934
0
    }
1935
1936
    // Creating SQLite tables and Insert cursors.
1937
0
    if (gmlHugeFileSQLiteInit(&helper) == false)
1938
0
    {
1939
0
        gmlHugeFileCleanUp(&helper);
1940
0
        return false;
1941
0
    }
1942
1943
    // Processing GML features.
1944
0
    GMLFeature *poFeature = nullptr;
1945
0
    while ((poFeature = NextFeature()) != nullptr)
1946
0
    {
1947
0
        const CPLXMLNode *const *papsGeomList = poFeature->GetGeometryList();
1948
0
        if (papsGeomList != nullptr)
1949
0
        {
1950
0
            int i = 0;
1951
0
            const CPLXMLNode *psNode = papsGeomList[i];
1952
0
            while (psNode)
1953
0
            {
1954
0
                gmlHugeFileCheckXrefs(&helper, psNode);
1955
                // Inserting into the SQLite DB any appropriate row.
1956
0
                gmlHugeFileSQLiteInsert(&helper);
1957
                // Resetting an empty helper struct.
1958
0
                gmlHugeFileReset(&helper);
1959
0
                i++;
1960
0
                psNode = papsGeomList[i];
1961
0
            }
1962
0
        }
1963
0
        delete poFeature;
1964
0
    }
1965
1966
    // Finalizing any SQLite Insert cursor.
1967
0
    if (helper.hNodes != nullptr)
1968
0
        sqlite3_finalize(helper.hNodes);
1969
0
    helper.hNodes = nullptr;
1970
0
    if (helper.hEdges != nullptr)
1971
0
        sqlite3_finalize(helper.hEdges);
1972
0
    helper.hEdges = nullptr;
1973
1974
    // Confirming the still pending TRANSACTION.
1975
0
    const int rc = sqlite3_exec(hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
1976
0
    if (rc != SQLITE_OK)
1977
0
    {
1978
0
        CPLError(CE_Failure, CPLE_AppDefined,
1979
0
                 "Unable to perform COMMIT TRANSACTION: %s", pszErrMsg);
1980
0
        sqlite3_free(pszErrMsg);
1981
0
        return false;
1982
0
    }
1983
1984
    // Attempting to resolve GML strings.
1985
0
    if (gmlHugeFileResolveEdges(&helper) == false)
1986
0
    {
1987
0
        gmlHugeFileCleanUp(&helper);
1988
0
        return false;
1989
0
    }
1990
1991
0
    CPLAssert(m_poGMLHandler);
1992
0
    const GMLAppSchemaType eAppSchemaType = m_poGMLHandler->GetAppSchemaType();
1993
1994
    // Restarting the GML parser.
1995
0
    if (!SetupParser())
1996
0
    {
1997
0
        gmlHugeFileCleanUp(&helper);
1998
0
        return false;
1999
0
    }
2000
2001
    // Output: writing the revolved GML file.
2002
0
    if (gmlHugeFileWriteResolved(&helper, pszOutputFilename, this,
2003
0
                                 eAppSchemaType,
2004
0
                                 &m_nHasSequentialLayers) == false)
2005
0
    {
2006
0
        gmlHugeFileCleanUp(&helper);
2007
0
        return false;
2008
0
    }
2009
2010
0
    gmlHugeFileCleanUp(&helper);
2011
0
    if (bSqliteIsTempFile)
2012
0
        VSIUnlink(pszSQLiteFilename);
2013
0
    return true;
2014
0
}
2015
2016
/**************************************************************/
2017
/*                                                            */
2018
/* an alternative <xlink:href> resolver based on SQLite       */
2019
/*                                                            */
2020
/**************************************************************/
2021
bool GMLReader::HugeFileResolver(const char *pszFile, bool bSqliteIsTempFile,
2022
                                 int iSqliteCacheMB)
2023
2024
0
{
2025
    // Check if the original source file is set.
2026
0
    if (m_pszFilename == nullptr)
2027
0
    {
2028
0
        CPLError(CE_Failure, CPLE_NotSupported,
2029
0
                 "GML source file needs to be set first with "
2030
0
                 "GMLReader::SetSourceFile().");
2031
0
        return false;
2032
0
    }
2033
0
    if (ParseXMLHugeFile(pszFile, bSqliteIsTempFile, iSqliteCacheMB) == false)
2034
0
        return false;
2035
2036
    // Set the source file to the resolved file.
2037
0
    CleanupParser();
2038
0
    if (fpGML)
2039
0
        VSIFCloseL(fpGML);
2040
0
    fpGML = nullptr;
2041
0
    CPLFree(m_pszFilename);
2042
0
    m_pszFilename = CPLStrdup(pszFile);
2043
0
    return true;
2044
0
}
2045
2046
#else  // HAVE_SQLITE
2047
2048
/**************************************************/
2049
/*    if SQLite support isn't available we'll     */
2050
/*    simply output an error message              */
2051
/**************************************************/
2052
2053
bool GMLReader::HugeFileResolver(CPL_UNUSED const char *pszFile,
2054
                                 CPL_UNUSED bool bSqliteIsTempFile,
2055
                                 CPL_UNUSED int iSqliteCacheMB)
2056
2057
{
2058
    CPLError(CE_Failure, CPLE_NotSupported,
2059
             "OGR was built without SQLite3 support. "
2060
             "Sorry, the HUGE GML resolver is unsupported.");
2061
    return false;
2062
}
2063
2064
bool GMLReader::ParseXMLHugeFile(CPL_UNUSED const char *pszOutputFilename,
2065
                                 CPL_UNUSED const bool bSqliteIsTempFile,
2066
                                 CPL_UNUSED const int iSqliteCacheMB)
2067
{
2068
    CPLError(CE_Failure, CPLE_NotSupported,
2069
             "OGR was built without SQLite3 support. "
2070
             "Sorry, the HUGE GML resolver is unsupported.");
2071
    return false;
2072
}
2073
2074
#endif  // HAVE_SQLITE