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