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