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