/src/gdal/ogr/ogr_srsnode.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: The OGR_SRSNode class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Les Technologies SoftMap Inc. |
9 | | * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "ogr_spatialref.h" |
16 | | |
17 | | #include <cctype> |
18 | | #include <cstddef> |
19 | | #include <cstring> |
20 | | |
21 | | #include "ogr_core.h" |
22 | | #include "ogr_p.h" |
23 | | #include "cpl_conv.h" |
24 | | #include "cpl_error.h" |
25 | | #include "cpl_string.h" |
26 | | |
27 | | /************************************************************************/ |
28 | | /* OGR_SRSNode() */ |
29 | | /************************************************************************/ |
30 | | |
31 | | /** |
32 | | * Constructor. |
33 | | * |
34 | | * @param pszValueIn this optional parameter can be used to initialize |
35 | | * the value of the node upon creation. If omitted the node will be created |
36 | | * with a value of "". Newly created OGR_SRSNodes have no children. |
37 | | */ |
38 | | |
39 | | OGR_SRSNode::OGR_SRSNode(const char *pszValueIn) |
40 | 18.6M | : pszValue(CPLStrdup(pszValueIn)), papoChildNodes(nullptr), |
41 | 18.6M | poParent(nullptr), nChildren(0) |
42 | 18.6M | { |
43 | 18.6M | } |
44 | | |
45 | | /************************************************************************/ |
46 | | /* ~OGR_SRSNode() */ |
47 | | /************************************************************************/ |
48 | | |
49 | | OGR_SRSNode::~OGR_SRSNode() |
50 | | |
51 | 18.6M | { |
52 | 18.6M | CPLFree(pszValue); |
53 | | |
54 | 18.6M | ClearChildren(); |
55 | 18.6M | } |
56 | | |
57 | | /************************************************************************/ |
58 | | /* ~Listener() */ |
59 | | /************************************************************************/ |
60 | | |
61 | 3.39M | OGR_SRSNode::Listener::~Listener() = default; |
62 | | |
63 | | /************************************************************************/ |
64 | | /* RegisterListener() */ |
65 | | /************************************************************************/ |
66 | | |
67 | | void OGR_SRSNode::RegisterListener(const std::shared_ptr<Listener> &listener) |
68 | 425k | { |
69 | 425k | m_listener = listener; |
70 | 425k | } |
71 | | |
72 | | /************************************************************************/ |
73 | | /* notifyChange() */ |
74 | | /************************************************************************/ |
75 | | |
76 | | void OGR_SRSNode::notifyChange() |
77 | 35.5M | { |
78 | 35.5M | auto locked = m_listener.lock(); |
79 | 35.5M | if (locked) |
80 | 34.4M | { |
81 | 34.4M | locked->notifyChange(this); |
82 | 34.4M | } |
83 | 35.5M | } |
84 | | |
85 | | /************************************************************************/ |
86 | | /* ClearChildren() */ |
87 | | /************************************************************************/ |
88 | | |
89 | | /** Clear children nodes |
90 | | */ |
91 | | void OGR_SRSNode::ClearChildren() |
92 | | |
93 | 35.9M | { |
94 | 54.0M | for (int i = 0; i < nChildren; i++) |
95 | 18.1M | { |
96 | 18.1M | delete papoChildNodes[i]; |
97 | 18.1M | } |
98 | | |
99 | 35.9M | CPLFree(papoChildNodes); |
100 | | |
101 | 35.9M | papoChildNodes = nullptr; |
102 | 35.9M | nChildren = 0; |
103 | 35.9M | } |
104 | | |
105 | | /************************************************************************/ |
106 | | /* GetChildCount() */ |
107 | | /************************************************************************/ |
108 | | |
109 | | /** |
110 | | * \fn int OGR_SRSNode::GetChildCount() const; |
111 | | * |
112 | | * Get number of children nodes. |
113 | | * |
114 | | * @return 0 for leaf nodes, or the number of children nodes. |
115 | | */ |
116 | | |
117 | | /************************************************************************/ |
118 | | /* GetChild() */ |
119 | | /************************************************************************/ |
120 | | |
121 | | /** |
122 | | * Fetch requested child. |
123 | | * |
124 | | * @param iChild the index of the child to fetch, from 0 to |
125 | | * GetChildCount() - 1. |
126 | | * |
127 | | * @return a pointer to the child OGR_SRSNode, or NULL if there is no such |
128 | | * child. |
129 | | */ |
130 | | |
131 | | OGR_SRSNode *OGR_SRSNode::GetChild(int iChild) |
132 | | |
133 | 1.87M | { |
134 | 1.87M | if (iChild < 0 || iChild >= nChildren) |
135 | 0 | return nullptr; |
136 | | |
137 | 1.87M | return papoChildNodes[iChild]; |
138 | 1.87M | } |
139 | | |
140 | | /** |
141 | | * Fetch requested child. |
142 | | * |
143 | | * @param iChild the index of the child to fetch, from 0 to |
144 | | * GetChildCount() - 1. |
145 | | * |
146 | | * @return a pointer to the child OGR_SRSNode, or NULL if there is no such |
147 | | * child. |
148 | | */ |
149 | | |
150 | | const OGR_SRSNode *OGR_SRSNode::GetChild(int iChild) const |
151 | | |
152 | 1.28M | { |
153 | 1.28M | if (iChild < 0 || iChild >= nChildren) |
154 | 0 | return nullptr; |
155 | | |
156 | 1.28M | return papoChildNodes[iChild]; |
157 | 1.28M | } |
158 | | |
159 | | /************************************************************************/ |
160 | | /* GetNode() */ |
161 | | /************************************************************************/ |
162 | | |
163 | | /** |
164 | | * Find named node in tree. |
165 | | * |
166 | | * This method does a pre-order traversal of the node tree searching for |
167 | | * a node with this exact value (case insensitive), and returns it. Leaf |
168 | | * nodes are not considered, under the assumption that they are just |
169 | | * attribute value nodes. |
170 | | * |
171 | | * If a node appears more than once in the tree (such as UNIT for instance), |
172 | | * the first encountered will be returned. Use GetNode() on a subtree to be |
173 | | * more specific. |
174 | | * |
175 | | * @param pszName the name of the node to search for. |
176 | | * |
177 | | * @return a pointer to the node found, or NULL if none. |
178 | | */ |
179 | | |
180 | | OGR_SRSNode *OGR_SRSNode::GetNode(const char *pszName) |
181 | | |
182 | 4.54M | { |
183 | 4.54M | if (nChildren > 0 && EQUAL(pszName, pszValue)) |
184 | 183k | return this; |
185 | | |
186 | | /* -------------------------------------------------------------------- */ |
187 | | /* First we check the immediate children so we will get an */ |
188 | | /* immediate child in preference to a subchild. */ |
189 | | /* -------------------------------------------------------------------- */ |
190 | 8.90M | for (int i = 0; i < nChildren; i++) |
191 | 4.79M | { |
192 | 4.79M | if (EQUAL(papoChildNodes[i]->pszValue, pszName) && |
193 | 248k | papoChildNodes[i]->nChildren > 0) |
194 | 248k | return papoChildNodes[i]; |
195 | 4.79M | } |
196 | | |
197 | | /* -------------------------------------------------------------------- */ |
198 | | /* Then get each child to check their children. */ |
199 | | /* -------------------------------------------------------------------- */ |
200 | 8.08M | for (int i = 0; i < nChildren; i++) |
201 | 4.01M | { |
202 | 4.01M | OGR_SRSNode *poNode = papoChildNodes[i]->GetNode(pszName); |
203 | 4.01M | if (poNode != nullptr) |
204 | 43.1k | return poNode; |
205 | 4.01M | } |
206 | | |
207 | 4.06M | return nullptr; |
208 | 4.10M | } |
209 | | |
210 | | /** |
211 | | * Find named node in tree. |
212 | | * |
213 | | * This method does a pre-order traversal of the node tree searching for |
214 | | * a node with this exact value (case insensitive), and returns it. Leaf |
215 | | * nodes are not considered, under the assumption that they are just |
216 | | * attribute value nodes. |
217 | | * |
218 | | * If a node appears more than once in the tree (such as UNIT for instance), |
219 | | * the first encountered will be returned. Use GetNode() on a subtree to be |
220 | | * more specific. |
221 | | * |
222 | | * @param pszName the name of the node to search for. |
223 | | * |
224 | | * @return a pointer to the node found, or NULL if none. |
225 | | */ |
226 | | |
227 | | const OGR_SRSNode *OGR_SRSNode::GetNode(const char *pszName) const |
228 | | |
229 | 0 | { |
230 | 0 | return const_cast<OGR_SRSNode *>(this)->GetNode(pszName); |
231 | 0 | } |
232 | | |
233 | | /************************************************************************/ |
234 | | /* AddChild() */ |
235 | | /************************************************************************/ |
236 | | |
237 | | /** |
238 | | * Add passed node as a child of target node. |
239 | | * |
240 | | * Note that ownership of the passed node is assumed by the node on which |
241 | | * the method is invoked ... use the Clone() method if the original is to |
242 | | * be preserved. New children are always added at the end of the list. |
243 | | * |
244 | | * @param poNew the node to add as a child. |
245 | | */ |
246 | | |
247 | | void OGR_SRSNode::AddChild(OGR_SRSNode *poNew) |
248 | | |
249 | 18.1M | { |
250 | 18.1M | InsertChild(poNew, nChildren); |
251 | 18.1M | } |
252 | | |
253 | | /************************************************************************/ |
254 | | /* InsertChild() */ |
255 | | /************************************************************************/ |
256 | | |
257 | | /** |
258 | | * Insert the passed node as a child of target node, at the indicated |
259 | | * position. |
260 | | * |
261 | | * Note that ownership of the passed node is assumed by the node on which |
262 | | * the method is invoked ... use the Clone() method if the original is to |
263 | | * be preserved. All existing children at location iChild and beyond are |
264 | | * push down one space to make space for the new child. |
265 | | * |
266 | | * @param poNew the node to add as a child. |
267 | | * @param iChild position to insert, use 0 to insert at the beginning. |
268 | | */ |
269 | | |
270 | | void OGR_SRSNode::InsertChild(OGR_SRSNode *poNew, int iChild) |
271 | | |
272 | 18.1M | { |
273 | 18.1M | if (iChild > nChildren) |
274 | 0 | iChild = nChildren; |
275 | | |
276 | 18.1M | nChildren++; |
277 | 18.1M | papoChildNodes = static_cast<OGR_SRSNode **>( |
278 | 18.1M | CPLRealloc(papoChildNodes, sizeof(void *) * nChildren)); |
279 | | |
280 | 18.1M | memmove(papoChildNodes + iChild + 1, papoChildNodes + iChild, |
281 | 18.1M | sizeof(void *) * (nChildren - iChild - 1)); |
282 | | |
283 | 18.1M | papoChildNodes[iChild] = poNew; |
284 | 18.1M | poNew->poParent = this; |
285 | | |
286 | 18.1M | poNew->m_listener = m_listener; |
287 | 18.1M | notifyChange(); |
288 | 18.1M | } |
289 | | |
290 | | /************************************************************************/ |
291 | | /* DestroyChild() */ |
292 | | /************************************************************************/ |
293 | | |
294 | | /** |
295 | | * Remove a child node, and it's subtree. |
296 | | * |
297 | | * Note that removing a child node will result in children after it |
298 | | * being renumbered down one. |
299 | | * |
300 | | * @param iChild the index of the child. |
301 | | */ |
302 | | |
303 | | void OGR_SRSNode::DestroyChild(int iChild) |
304 | | |
305 | 56.0k | { |
306 | 56.0k | if (iChild < 0 || iChild >= nChildren) |
307 | 0 | return; |
308 | | |
309 | 56.0k | delete papoChildNodes[iChild]; |
310 | 106k | while (iChild < nChildren - 1) |
311 | 50.2k | { |
312 | 50.2k | papoChildNodes[iChild] = papoChildNodes[iChild + 1]; |
313 | 50.2k | iChild++; |
314 | 50.2k | } |
315 | | |
316 | 56.0k | nChildren--; |
317 | 56.0k | notifyChange(); |
318 | 56.0k | } |
319 | | |
320 | | /************************************************************************/ |
321 | | /* FindChild() */ |
322 | | /************************************************************************/ |
323 | | |
324 | | /** |
325 | | * Find the index of the child matching the given string. |
326 | | * |
327 | | * Note that the node value must match pszValue with the exception of |
328 | | * case. The comparison is case insensitive. |
329 | | * |
330 | | * @param pszValueIn the node value being searched for. |
331 | | * |
332 | | * @return the child index, or -1 on failure. |
333 | | */ |
334 | | |
335 | | int OGR_SRSNode::FindChild(const char *pszValueIn) const |
336 | | |
337 | 1.16M | { |
338 | 3.11M | for (int i = 0; i < nChildren; i++) |
339 | 2.06M | { |
340 | 2.06M | if (EQUAL(papoChildNodes[i]->pszValue, pszValueIn)) |
341 | 122k | return i; |
342 | 2.06M | } |
343 | | |
344 | 1.04M | return -1; |
345 | 1.16M | } |
346 | | |
347 | | /************************************************************************/ |
348 | | /* GetValue() */ |
349 | | /************************************************************************/ |
350 | | |
351 | | /** |
352 | | * \fn const char *OGR_SRSNode::GetValue() const; |
353 | | * |
354 | | * Fetch value string for this node. |
355 | | * |
356 | | * @return A non-NULL string is always returned. The returned pointer is to |
357 | | * the internal value of this node, and should not be modified, or freed. |
358 | | */ |
359 | | |
360 | | /************************************************************************/ |
361 | | /* SetValue() */ |
362 | | /************************************************************************/ |
363 | | |
364 | | /** |
365 | | * Set the node value. |
366 | | * |
367 | | * @param pszNewValue the new value to assign to this node. The passed |
368 | | * string is duplicated and remains the responsibility of the caller. |
369 | | */ |
370 | | |
371 | | void OGR_SRSNode::SetValue(const char *pszNewValue) |
372 | | |
373 | 17.2M | { |
374 | 17.2M | CPLFree(pszValue); |
375 | 17.2M | pszValue = CPLStrdup(pszNewValue); |
376 | 17.2M | notifyChange(); |
377 | 17.2M | } |
378 | | |
379 | | /************************************************************************/ |
380 | | /* Clone() */ |
381 | | /************************************************************************/ |
382 | | |
383 | | /** |
384 | | * Make a duplicate of this node, and it's children. |
385 | | * |
386 | | * @return a new node tree, which becomes the responsibility of the caller. |
387 | | */ |
388 | | |
389 | | OGR_SRSNode *OGR_SRSNode::Clone() const |
390 | | |
391 | 907k | { |
392 | 907k | OGR_SRSNode *poNew = new OGR_SRSNode(pszValue); |
393 | | |
394 | 1.79M | for (int i = 0; i < nChildren; i++) |
395 | 886k | { |
396 | 886k | poNew->AddChild(papoChildNodes[i]->Clone()); |
397 | 886k | } |
398 | 907k | poNew->m_listener = m_listener; |
399 | | |
400 | 907k | return poNew; |
401 | 907k | } |
402 | | |
403 | | /************************************************************************/ |
404 | | /* NeedsQuoting() */ |
405 | | /* */ |
406 | | /* Does this node need to be quoted when it is exported to Wkt? */ |
407 | | /************************************************************************/ |
408 | | |
409 | | int OGR_SRSNode::NeedsQuoting() const |
410 | | |
411 | 6.27M | { |
412 | | // Non-terminals are never quoted. |
413 | 6.27M | if (GetChildCount() != 0) |
414 | 1.97M | return FALSE; |
415 | | |
416 | | // As per bugzilla bug 201, the OGC spec says the authority code |
417 | | // needs to be quoted even though it appears well behaved. |
418 | 4.29M | if (poParent != nullptr && EQUAL(poParent->GetValue(), "AUTHORITY")) |
419 | 684k | return TRUE; |
420 | | |
421 | | // As per bugzilla bug 294, the OGC spec says the direction |
422 | | // values for the AXIS keywords should *not* be quoted. |
423 | 3.60M | if (poParent != nullptr && EQUAL(poParent->GetValue(), "AXIS") && |
424 | 521k | this != poParent->GetChild(0)) |
425 | 275k | return FALSE; |
426 | | |
427 | 3.33M | if (poParent != nullptr && EQUAL(poParent->GetValue(), "CS") && |
428 | 26.5k | this == poParent->GetChild(0)) |
429 | 4.64k | return FALSE; |
430 | | |
431 | | // Strings starting with e or E are not valid numeric values, so they |
432 | | // need quoting, like in AXIS["E",EAST] |
433 | 3.32M | if ((pszValue[0] == 'e' || pszValue[0] == 'E')) |
434 | 137k | return TRUE; |
435 | | |
436 | | // Non-numeric tokens are generally quoted while clean numeric values |
437 | | // are generally not. |
438 | 9.47M | for (int i = 0; pszValue[i] != '\0'; i++) |
439 | 8.06M | { |
440 | 8.06M | if ((pszValue[i] < '0' || pszValue[i] > '9') && pszValue[i] != '.' && |
441 | 2.02M | pszValue[i] != '-' && pszValue[i] != '+' && pszValue[i] != 'e' && |
442 | 1.79M | pszValue[i] != 'E') |
443 | 1.77M | return TRUE; |
444 | 8.06M | } |
445 | | |
446 | 1.41M | return FALSE; |
447 | 3.19M | } |
448 | | |
449 | | /************************************************************************/ |
450 | | /* exportToWkt() */ |
451 | | /************************************************************************/ |
452 | | |
453 | | /** |
454 | | * Convert this tree of nodes into WKT format. |
455 | | * |
456 | | * Note that the returned WKT string should be freed with |
457 | | * CPLFree() when no longer needed. It is the responsibility of the caller. |
458 | | * |
459 | | * @param ppszResult the resulting string is returned in this pointer. |
460 | | * |
461 | | * @return currently OGRERR_NONE is always returned, but the future it |
462 | | * is possible error conditions will develop. |
463 | | */ |
464 | | |
465 | | OGRErr OGR_SRSNode::exportToWkt(char **ppszResult) const |
466 | | |
467 | 6.27M | { |
468 | | /* -------------------------------------------------------------------- */ |
469 | | /* Build a list of the WKT format for the children. */ |
470 | | /* -------------------------------------------------------------------- */ |
471 | 6.27M | char **papszChildrenWkt = |
472 | 6.27M | static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1)); |
473 | 6.27M | size_t nLength = strlen(pszValue) + 4; |
474 | | |
475 | 12.4M | for (int i = 0; i < nChildren; i++) |
476 | 6.15M | { |
477 | 6.15M | papoChildNodes[i]->exportToWkt(papszChildrenWkt + i); |
478 | 6.15M | nLength += strlen(papszChildrenWkt[i]) + 1; |
479 | 6.15M | } |
480 | | |
481 | | /* -------------------------------------------------------------------- */ |
482 | | /* Allocate the result string. */ |
483 | | /* -------------------------------------------------------------------- */ |
484 | 6.27M | *ppszResult = static_cast<char *>(CPLMalloc(nLength)); |
485 | 6.27M | *ppszResult[0] = '\0'; |
486 | | |
487 | | /* -------------------------------------------------------------------- */ |
488 | | /* Capture this nodes value. We put it in double quotes if */ |
489 | | /* this is a leaf node, otherwise we assume it is a well formed */ |
490 | | /* node name. */ |
491 | | /* -------------------------------------------------------------------- */ |
492 | 6.27M | if (NeedsQuoting()) |
493 | 2.60M | { |
494 | 2.60M | strcat(*ppszResult, "\""); |
495 | 2.60M | strcat(*ppszResult, pszValue); // Should we do quoting? |
496 | 2.60M | strcat(*ppszResult, "\""); |
497 | 2.60M | } |
498 | 3.67M | else |
499 | 3.67M | strcat(*ppszResult, pszValue); |
500 | | |
501 | | /* -------------------------------------------------------------------- */ |
502 | | /* Add the children strings with appropriate brackets and commas. */ |
503 | | /* -------------------------------------------------------------------- */ |
504 | 6.27M | if (nChildren > 0) |
505 | 1.97M | strcat(*ppszResult, "["); |
506 | | |
507 | 12.4M | for (int i = 0; i < nChildren; i++) |
508 | 6.15M | { |
509 | 6.15M | strcat(*ppszResult, papszChildrenWkt[i]); |
510 | 6.15M | if (i == nChildren - 1) |
511 | 1.97M | strcat(*ppszResult, "]"); |
512 | 4.17M | else |
513 | 4.17M | strcat(*ppszResult, ","); |
514 | 6.15M | } |
515 | | |
516 | 6.27M | CSLDestroy(papszChildrenWkt); |
517 | | |
518 | 6.27M | return OGRERR_NONE; |
519 | 6.27M | } |
520 | | |
521 | | /************************************************************************/ |
522 | | /* exportToPrettyWkt() */ |
523 | | /************************************************************************/ |
524 | | |
525 | | /** |
526 | | * Convert this tree of nodes into pretty WKT format. |
527 | | * |
528 | | * Note that the returned WKT string should be freed with |
529 | | * CPLFree() when no longer needed. It is the responsibility of the caller. |
530 | | * |
531 | | * @param ppszResult the resulting string is returned in this pointer. |
532 | | * |
533 | | * @param nDepth depth of the node |
534 | | * |
535 | | * @return currently OGRERR_NONE is always returned, but the future it |
536 | | * is possible error conditions will develop. |
537 | | */ |
538 | | |
539 | | OGRErr OGR_SRSNode::exportToPrettyWkt(char **ppszResult, int nDepth) const |
540 | | |
541 | 0 | { |
542 | | /* -------------------------------------------------------------------- */ |
543 | | /* Build a list of the WKT format for the children. */ |
544 | | /* -------------------------------------------------------------------- */ |
545 | 0 | char **papszChildrenWkt = |
546 | 0 | static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1)); |
547 | 0 | size_t nLength = strlen(pszValue) + 4; |
548 | |
|
549 | 0 | for (int i = 0; i < nChildren; i++) |
550 | 0 | { |
551 | 0 | papoChildNodes[i]->exportToPrettyWkt(papszChildrenWkt + i, nDepth + 1); |
552 | 0 | nLength += strlen(papszChildrenWkt[i]) + 2 + nDepth * 4; |
553 | 0 | } |
554 | | |
555 | | /* -------------------------------------------------------------------- */ |
556 | | /* Allocate the result string. */ |
557 | | /* -------------------------------------------------------------------- */ |
558 | 0 | *ppszResult = static_cast<char *>(CPLMalloc(nLength)); |
559 | 0 | *ppszResult[0] = '\0'; |
560 | | |
561 | | /* -------------------------------------------------------------------- */ |
562 | | /* Capture this nodes value. We put it in double quotes if */ |
563 | | /* this is a leaf node, otherwise we assume it is a well formed */ |
564 | | /* node name. */ |
565 | | /* -------------------------------------------------------------------- */ |
566 | 0 | if (NeedsQuoting()) |
567 | 0 | { |
568 | 0 | strcat(*ppszResult, "\""); |
569 | 0 | strcat(*ppszResult, pszValue); // Should we do quoting? |
570 | 0 | strcat(*ppszResult, "\""); |
571 | 0 | } |
572 | 0 | else |
573 | 0 | { |
574 | 0 | strcat(*ppszResult, pszValue); |
575 | 0 | } |
576 | | |
577 | | /* -------------------------------------------------------------------- */ |
578 | | /* Add the children strings with appropriate brackets and commas. */ |
579 | | /* -------------------------------------------------------------------- */ |
580 | 0 | if (nChildren > 0) |
581 | 0 | strcat(*ppszResult, "["); |
582 | |
|
583 | 0 | for (int i = 0; i < nChildren; i++) |
584 | 0 | { |
585 | 0 | if (papoChildNodes[i]->GetChildCount() > 0) |
586 | 0 | { |
587 | 0 | strcat(*ppszResult, "\n"); |
588 | 0 | for (int j = 0; j < 4 * nDepth; j++) |
589 | 0 | strcat(*ppszResult, " "); |
590 | 0 | } |
591 | 0 | strcat(*ppszResult, papszChildrenWkt[i]); |
592 | 0 | if (i < nChildren - 1) |
593 | 0 | strcat(*ppszResult, ","); |
594 | 0 | } |
595 | |
|
596 | 0 | if (nChildren > 0) |
597 | 0 | { |
598 | 0 | if ((*ppszResult)[strlen(*ppszResult) - 1] == ',') |
599 | 0 | (*ppszResult)[strlen(*ppszResult) - 1] = '\0'; |
600 | |
|
601 | 0 | strcat(*ppszResult, "]"); |
602 | 0 | } |
603 | |
|
604 | 0 | CSLDestroy(papszChildrenWkt); |
605 | |
|
606 | 0 | return OGRERR_NONE; |
607 | 0 | } |
608 | | |
609 | | /************************************************************************/ |
610 | | /* importFromWkt() */ |
611 | | /************************************************************************/ |
612 | | |
613 | | /** |
614 | | * Import from WKT string. |
615 | | * |
616 | | * This method will wipe the existing children and value of this node, and |
617 | | * reassign them based on the contents of the passed WKT string. Only as |
618 | | * much of the input string as needed to construct this node, and its |
619 | | * children is consumed from the input string, and the input string pointer |
620 | | * is then updated to point to the remaining (unused) input. |
621 | | * |
622 | | * @param ppszInput Pointer to pointer to input. The pointer is updated to |
623 | | * point to remaining unused input text. |
624 | | * |
625 | | * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it |
626 | | * fails for any reason. |
627 | | * @deprecated Use importFromWkt(const char**) instead. |
628 | | */ |
629 | | |
630 | | OGRErr OGR_SRSNode::importFromWkt(char **ppszInput) |
631 | | |
632 | 0 | { |
633 | 0 | int nNodes = 0; |
634 | 0 | return importFromWkt(const_cast<const char **>(ppszInput), 0, &nNodes); |
635 | 0 | } |
636 | | |
637 | | /** |
638 | | * Import from WKT string. |
639 | | * |
640 | | * This method will wipe the existing children and value of this node, and |
641 | | * reassign them based on the contents of the passed WKT string. Only as |
642 | | * much of the input string as needed to construct this node, and its |
643 | | * children is consumed from the input string, and the input string pointer |
644 | | * is then updated to point to the remaining (unused) input. |
645 | | * |
646 | | * @param ppszInput Pointer to pointer to input. The pointer is updated to |
647 | | * point to remaining unused input text. |
648 | | * |
649 | | * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it |
650 | | * fails for any reason. |
651 | | * |
652 | | */ |
653 | | |
654 | | OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput) |
655 | | |
656 | 406k | { |
657 | 406k | int nNodes = 0; |
658 | 406k | return importFromWkt(ppszInput, 0, &nNodes); |
659 | 406k | } |
660 | | |
661 | | OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput, int nRecLevel, |
662 | | int *pnNodes) |
663 | | |
664 | 17.2M | { |
665 | | // Sanity checks. |
666 | 17.2M | if (nRecLevel == 10) |
667 | 73 | { |
668 | 73 | return OGRERR_CORRUPT_DATA; |
669 | 73 | } |
670 | 17.2M | if (*pnNodes == 1000) |
671 | 315 | { |
672 | 315 | return OGRERR_CORRUPT_DATA; |
673 | 315 | } |
674 | | |
675 | 17.2M | const char *pszInput = *ppszInput; |
676 | 17.2M | bool bInQuotedString = false; |
677 | | |
678 | | /* -------------------------------------------------------------------- */ |
679 | | /* Clear any existing children of this node. */ |
680 | | /* -------------------------------------------------------------------- */ |
681 | 17.2M | ClearChildren(); |
682 | | |
683 | | /* -------------------------------------------------------------------- */ |
684 | | /* Read the ``value'' for this node. */ |
685 | | /* -------------------------------------------------------------------- */ |
686 | 17.2M | { |
687 | 17.2M | char szToken[512]; // do not initialize whole buffer. significant |
688 | | // overhead |
689 | 17.2M | size_t nTokenLen = 0; |
690 | 17.2M | szToken[0] = '\0'; |
691 | | |
692 | 187M | while (*pszInput != '\0' && nTokenLen + 1 < sizeof(szToken)) |
693 | 187M | { |
694 | 187M | if (*pszInput == '"') |
695 | 14.1M | { |
696 | 14.1M | bInQuotedString = !bInQuotedString; |
697 | 14.1M | } |
698 | 173M | else if (!bInQuotedString && |
699 | 86.7M | (*pszInput == '[' || *pszInput == ']' || |
700 | 77.1M | *pszInput == ',' || *pszInput == '(' || *pszInput == ')')) |
701 | 17.2M | { |
702 | 17.2M | break; |
703 | 17.2M | } |
704 | 156M | else if (!bInQuotedString && |
705 | 69.5M | (*pszInput == ' ' || *pszInput == '\t' || |
706 | 69.2M | *pszInput == 10 || *pszInput == 13)) |
707 | 353k | { |
708 | | // Skip over whitespace. |
709 | 353k | } |
710 | 156M | else |
711 | 156M | { |
712 | 156M | szToken[nTokenLen++] = *pszInput; |
713 | 156M | } |
714 | | |
715 | 170M | pszInput++; |
716 | 170M | } |
717 | | |
718 | 17.2M | if (*pszInput == '\0' || nTokenLen == sizeof(szToken) - 1) |
719 | 7.81k | return OGRERR_CORRUPT_DATA; |
720 | | |
721 | 17.2M | szToken[nTokenLen++] = '\0'; |
722 | 17.2M | SetValue(szToken); |
723 | 17.2M | } |
724 | | |
725 | | /* -------------------------------------------------------------------- */ |
726 | | /* Read children, if we have a sublist. */ |
727 | | /* -------------------------------------------------------------------- */ |
728 | 17.2M | if (*pszInput == '[' || *pszInput == '(') |
729 | 5.93M | { |
730 | 5.93M | do |
731 | 16.8M | { |
732 | 16.8M | pszInput++; // Skip bracket or comma. |
733 | | |
734 | 16.8M | OGR_SRSNode *poNewChild = new OGR_SRSNode(); |
735 | 16.8M | poNewChild->m_listener = m_listener; |
736 | | |
737 | 16.8M | (*pnNodes)++; |
738 | 16.8M | const OGRErr eErr = |
739 | 16.8M | poNewChild->importFromWkt(&pszInput, nRecLevel + 1, pnNodes); |
740 | 16.8M | if (eErr != OGRERR_NONE) |
741 | 18.4k | { |
742 | 18.4k | delete poNewChild; |
743 | 18.4k | return eErr; |
744 | 18.4k | } |
745 | | |
746 | 16.8M | AddChild(poNewChild); |
747 | | |
748 | | // Swallow whitespace. |
749 | 16.9M | while (isspace(static_cast<unsigned char>(*pszInput))) |
750 | 71.8k | pszInput++; |
751 | 16.8M | } while (*pszInput == ','); |
752 | | |
753 | 5.91M | if (*pszInput != ')' && *pszInput != ']') |
754 | 3.45k | return OGRERR_CORRUPT_DATA; |
755 | | |
756 | 5.91M | pszInput++; |
757 | 5.91M | } |
758 | | |
759 | 17.2M | *ppszInput = pszInput; |
760 | | |
761 | 17.2M | return OGRERR_NONE; |
762 | 17.2M | } |
763 | | |
764 | | /************************************************************************/ |
765 | | /* MakeValueSafe() */ |
766 | | /************************************************************************/ |
767 | | |
768 | | /** |
769 | | * Massage value string, stripping special characters so it will be a |
770 | | * database safe string. |
771 | | * |
772 | | * The operation is also applies to all subnodes of the current node. |
773 | | */ |
774 | | |
775 | | void OGR_SRSNode::MakeValueSafe() |
776 | | |
777 | 0 | { |
778 | | /* -------------------------------------------------------------------- */ |
779 | | /* First process subnodes. */ |
780 | | /* -------------------------------------------------------------------- */ |
781 | 0 | for (int iChild = 0; iChild < GetChildCount(); iChild++) |
782 | 0 | { |
783 | 0 | GetChild(iChild)->MakeValueSafe(); |
784 | 0 | } |
785 | | |
786 | | /* -------------------------------------------------------------------- */ |
787 | | /* Skip numeric nodes. */ |
788 | | /* -------------------------------------------------------------------- */ |
789 | 0 | if ((pszValue[0] >= '0' && pszValue[0] <= '9') || pszValue[0] != '.') |
790 | 0 | return; |
791 | | |
792 | | /* -------------------------------------------------------------------- */ |
793 | | /* Translate non-alphanumeric values to underscores. */ |
794 | | /* -------------------------------------------------------------------- */ |
795 | 0 | for (int i = 0; pszValue[i] != '\0'; i++) |
796 | 0 | { |
797 | 0 | if (!(pszValue[i] >= 'A' && pszValue[i] <= 'Z') && |
798 | 0 | !(pszValue[i] >= 'a' && pszValue[i] <= 'z') && |
799 | 0 | !(pszValue[i] >= '0' && pszValue[i] <= '9')) |
800 | 0 | { |
801 | 0 | pszValue[i] = '_'; |
802 | 0 | } |
803 | 0 | } |
804 | | |
805 | | /* -------------------------------------------------------------------- */ |
806 | | /* Remove repeated and trailing underscores. */ |
807 | | /* -------------------------------------------------------------------- */ |
808 | 0 | int j = 0; |
809 | 0 | for (int i = 1; pszValue[i] != '\0'; i++) |
810 | 0 | { |
811 | 0 | if (pszValue[j] == '_' && pszValue[i] == '_') |
812 | 0 | continue; |
813 | | |
814 | 0 | pszValue[++j] = pszValue[i]; |
815 | 0 | } |
816 | |
|
817 | 0 | if (pszValue[j] == '_') |
818 | 0 | pszValue[j] = '\0'; |
819 | 0 | else |
820 | 0 | pszValue[j + 1] = '\0'; |
821 | 0 | } |
822 | | |
823 | | /************************************************************************/ |
824 | | /* StripNodes() */ |
825 | | /************************************************************************/ |
826 | | |
827 | | /** |
828 | | * Strip child nodes matching name. |
829 | | * |
830 | | * Removes any descendant nodes of this node that match the given name. |
831 | | * Of course children of removed nodes are also discarded. |
832 | | * |
833 | | * @param pszName the name for nodes that should be removed. |
834 | | */ |
835 | | |
836 | | void OGR_SRSNode::StripNodes(const char *pszName) |
837 | | |
838 | 973k | { |
839 | | /* -------------------------------------------------------------------- */ |
840 | | /* Strip any children matching this name. */ |
841 | | /* -------------------------------------------------------------------- */ |
842 | 991k | while (FindChild(pszName) >= 0) |
843 | 18.2k | DestroyChild(FindChild(pszName)); |
844 | | |
845 | | /* -------------------------------------------------------------------- */ |
846 | | /* Recurse */ |
847 | | /* -------------------------------------------------------------------- */ |
848 | 1.91M | for (int i = 0; i < GetChildCount(); i++) |
849 | 936k | GetChild(i)->StripNodes(pszName); |
850 | 973k | } |