Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GML Reader
4 : * Purpose: Code to translate between GML and OGR geometry forms.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam
9 : * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : *****************************************************************************
13 : *
14 : * Independent Security Audit 2003/04/17 Andrey Kiselev:
15 : * Completed audit of this module. All functions may be used without buffer
16 : * overflows and stack corruptions with any kind of input data.
17 : *
18 : * Security Audit 2003/03/28 warmerda:
19 : * Completed security audit. I believe that this module may be safely used
20 : * to parse, arbitrary GML potentially provided by a hostile source without
21 : * compromising the system.
22 : *
23 : */
24 :
25 : #include "cpl_port.h"
26 : #include "ogr_api.h"
27 :
28 : #include <algorithm>
29 : #include <cassert>
30 : #include <cctype>
31 : #include <cmath>
32 : #include <cstdlib>
33 : #include <cstring>
34 :
35 : #include "cpl_conv.h"
36 : #include "cpl_error.h"
37 : #include "cpl_mem_cache.h"
38 : #include "cpl_minixml.h"
39 : #include "cpl_string.h"
40 : #include "ogr_core.h"
41 : #include "ogr_geometry.h"
42 : #include "ogr_p.h"
43 : #include "ogr_spatialref.h"
44 : #include "ogr_srs_api.h"
45 : #include "ogr_geo_utils.h"
46 :
47 : constexpr double kdfD2R = M_PI / 180.0;
48 : constexpr double kdf2PI = 2.0 * M_PI;
49 :
50 : /************************************************************************/
51 : /* GMLGetCoordTokenPos() */
52 : /************************************************************************/
53 :
54 813974 : static const char *GMLGetCoordTokenPos(const char *pszStr,
55 : const char **ppszNextToken)
56 : {
57 : char ch;
58 : while (true)
59 : {
60 : // cppcheck-suppress nullPointerRedundantCheck
61 813974 : ch = *pszStr;
62 813974 : if (ch == '\0')
63 : {
64 2180 : *ppszNextToken = pszStr;
65 2180 : return nullptr;
66 : }
67 811794 : else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' ||
68 : ch == ','))
69 407177 : break;
70 404617 : pszStr++;
71 : }
72 :
73 407177 : const char *pszToken = pszStr;
74 4186180 : while ((ch = *pszStr) != '\0')
75 : {
76 4183130 : if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',')
77 : {
78 404124 : *ppszNextToken = pszStr;
79 404124 : return pszToken;
80 : }
81 3779000 : pszStr++;
82 : }
83 3053 : *ppszNextToken = pszStr;
84 3053 : return pszToken;
85 : }
86 :
87 : /************************************************************************/
88 : /* BareGMLElement() */
89 : /* */
90 : /* Returns the passed string with any namespace prefix */
91 : /* stripped off. */
92 : /************************************************************************/
93 :
94 31827 : static const char *BareGMLElement(const char *pszInput)
95 :
96 : {
97 31827 : const char *pszReturn = strchr(pszInput, ':');
98 31827 : if (pszReturn == nullptr)
99 23518 : pszReturn = pszInput;
100 : else
101 8309 : pszReturn++;
102 :
103 31827 : return pszReturn;
104 : }
105 :
106 : /************************************************************************/
107 : /* FindBareXMLChild() */
108 : /* */
109 : /* Find a child node with the indicated "bare" name, that is */
110 : /* after any namespace qualifiers have been stripped off. */
111 : /************************************************************************/
112 :
113 12597 : static const CPLXMLNode *FindBareXMLChild(const CPLXMLNode *psParent,
114 : const char *pszBareName)
115 :
116 : {
117 12597 : const CPLXMLNode *psCandidate = psParent->psChild;
118 :
119 27132 : while (psCandidate != nullptr)
120 : {
121 38119 : if (psCandidate->eType == CXT_Element &&
122 15392 : EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName))
123 8192 : return psCandidate;
124 :
125 14535 : psCandidate = psCandidate->psNext;
126 : }
127 :
128 4405 : return nullptr;
129 : }
130 :
131 : /************************************************************************/
132 : /* GetElementText() */
133 : /************************************************************************/
134 :
135 3668 : static const char *GetElementText(const CPLXMLNode *psElement)
136 :
137 : {
138 3668 : if (psElement == nullptr)
139 0 : return nullptr;
140 :
141 3668 : const CPLXMLNode *psChild = psElement->psChild;
142 :
143 5619 : while (psChild != nullptr)
144 : {
145 5611 : if (psChild->eType == CXT_Text)
146 3660 : return psChild->pszValue;
147 :
148 1951 : psChild = psChild->psNext;
149 : }
150 :
151 8 : return nullptr;
152 : }
153 :
154 : /************************************************************************/
155 : /* GetChildElement() */
156 : /************************************************************************/
157 :
158 2021 : static const CPLXMLNode *GetChildElement(const CPLXMLNode *psElement)
159 :
160 : {
161 2021 : if (psElement == nullptr)
162 13 : return nullptr;
163 :
164 2008 : const CPLXMLNode *psChild = psElement->psChild;
165 :
166 2221 : while (psChild != nullptr)
167 : {
168 2203 : if (psChild->eType == CXT_Element)
169 1990 : return psChild;
170 :
171 213 : psChild = psChild->psNext;
172 : }
173 :
174 18 : return nullptr;
175 : }
176 :
177 : /************************************************************************/
178 : /* GetElementOrientation() */
179 : /* Returns true for positive orientation. */
180 : /************************************************************************/
181 :
182 1266 : static bool GetElementOrientation(const CPLXMLNode *psElement)
183 : {
184 1266 : if (psElement == nullptr)
185 0 : return true;
186 :
187 1266 : const CPLXMLNode *psChild = psElement->psChild;
188 :
189 2124 : while (psChild != nullptr)
190 : {
191 1270 : if (psChild->eType == CXT_Attribute &&
192 416 : EQUAL(psChild->pszValue, "orientation"))
193 412 : return EQUAL(psChild->psChild->pszValue, "+");
194 :
195 858 : psChild = psChild->psNext;
196 : }
197 :
198 854 : return true;
199 : }
200 :
201 : /************************************************************************/
202 : /* AddPoint() */
203 : /* */
204 : /* Add a point to the passed geometry. */
205 : /************************************************************************/
206 :
207 193642 : static bool AddPoint(OGRGeometry *poGeometry, double dfX, double dfY,
208 : double dfZ, int nDimension)
209 :
210 : {
211 193642 : const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
212 193642 : if (eType == wkbPoint)
213 : {
214 1066 : OGRPoint *poPoint = poGeometry->toPoint();
215 :
216 1066 : if (!poPoint->IsEmpty())
217 : {
218 2 : CPLError(CE_Failure, CPLE_AppDefined,
219 : "More than one coordinate for <Point> element.");
220 2 : return false;
221 : }
222 :
223 1064 : poPoint->setX(dfX);
224 1064 : poPoint->setY(dfY);
225 1064 : if (nDimension == 3)
226 25 : poPoint->setZ(dfZ);
227 :
228 1064 : return true;
229 : }
230 192576 : else if (eType == wkbLineString || eType == wkbCircularString)
231 : {
232 192576 : OGRSimpleCurve *poCurve = poGeometry->toSimpleCurve();
233 192576 : if (nDimension == 3)
234 20707 : poCurve->addPoint(dfX, dfY, dfZ);
235 : else
236 171869 : poCurve->addPoint(dfX, dfY);
237 :
238 192576 : return true;
239 : }
240 :
241 0 : CPLAssert(false);
242 : return false;
243 : }
244 :
245 : /************************************************************************/
246 : /* ParseGMLCoordinates() */
247 : /************************************************************************/
248 :
249 3465 : static bool ParseGMLCoordinates(const CPLXMLNode *psGeomNode,
250 : OGRGeometry *poGeometry, int nSRSDimension)
251 :
252 : {
253 : const CPLXMLNode *psCoordinates =
254 3465 : FindBareXMLChild(psGeomNode, "coordinates");
255 :
256 : /* -------------------------------------------------------------------- */
257 : /* Handle <coordinates> case. */
258 : /* Note that we don't do a strict validation, so we accept and */
259 : /* sometimes generate output whereas we should just reject it. */
260 : /* -------------------------------------------------------------------- */
261 3465 : if (psCoordinates != nullptr)
262 : {
263 528 : const char *pszCoordString = GetElementText(psCoordinates);
264 :
265 : const char *pszDecimal =
266 528 : CPLGetXMLValue(psCoordinates, "decimal", nullptr);
267 528 : char chDecimal = '.';
268 528 : if (pszDecimal != nullptr)
269 : {
270 35 : if (strlen(pszDecimal) != 1 ||
271 33 : (pszDecimal[0] >= '0' && pszDecimal[0] <= '9'))
272 : {
273 3 : CPLError(CE_Failure, CPLE_AppDefined,
274 : "Wrong value for decimal attribute");
275 3 : return false;
276 : }
277 32 : chDecimal = pszDecimal[0];
278 : }
279 :
280 525 : const char *pszCS = CPLGetXMLValue(psCoordinates, "cs", nullptr);
281 525 : char chCS = ',';
282 525 : if (pszCS != nullptr)
283 : {
284 38 : if (strlen(pszCS) != 1 || (pszCS[0] >= '0' && pszCS[0] <= '9'))
285 : {
286 3 : CPLError(CE_Failure, CPLE_AppDefined,
287 : "Wrong value for cs attribute");
288 3 : return false;
289 : }
290 35 : chCS = pszCS[0];
291 : }
292 522 : const char *pszTS = CPLGetXMLValue(psCoordinates, "ts", nullptr);
293 522 : char chTS = ' ';
294 522 : if (pszTS != nullptr)
295 : {
296 36 : if (strlen(pszTS) != 1 || (pszTS[0] >= '0' && pszTS[0] <= '9'))
297 : {
298 3 : CPLError(CE_Failure, CPLE_AppDefined,
299 : "Wrong value for ts attribute");
300 3 : return false;
301 : }
302 33 : chTS = pszTS[0];
303 : }
304 :
305 519 : if (pszCoordString == nullptr)
306 : {
307 1 : poGeometry->empty();
308 1 : return true;
309 : }
310 :
311 : // Skip leading whitespace. See
312 : // https://github.com/OSGeo/gdal/issues/5494
313 535 : while (*pszCoordString != '\0' &&
314 535 : isspace(static_cast<unsigned char>(*pszCoordString)))
315 : {
316 17 : pszCoordString++;
317 : }
318 :
319 518 : int iCoord = 0;
320 : const OGRwkbGeometryType eType =
321 518 : wkbFlatten(poGeometry->getGeometryType());
322 : OGRSimpleCurve *poCurve =
323 415 : (eType == wkbLineString || eType == wkbCircularString)
324 933 : ? poGeometry->toSimpleCurve()
325 518 : : nullptr;
326 1137 : for (int iter = (eType == wkbPoint ? 1 : 0); iter < 2; iter++)
327 : {
328 621 : const char *pszStr = pszCoordString;
329 621 : double dfX = 0;
330 621 : double dfY = 0;
331 621 : iCoord = 0;
332 2359 : while (*pszStr != '\0')
333 : {
334 1740 : int nDimension = 2;
335 : // parse out 2 or 3 tuple.
336 1740 : if (iter == 1)
337 : {
338 1078 : if (chDecimal == '.')
339 1076 : dfX = OGRFastAtof(pszStr);
340 : else
341 2 : dfX = CPLAtofDelim(pszStr, chDecimal);
342 : }
343 13727 : while (*pszStr != '\0' && *pszStr != chCS &&
344 11992 : !isspace(static_cast<unsigned char>(*pszStr)))
345 11987 : pszStr++;
346 :
347 1740 : if (*pszStr == '\0')
348 : {
349 1 : CPLError(CE_Failure, CPLE_AppDefined,
350 : "Corrupt <coordinates> value.");
351 1 : return false;
352 : }
353 1739 : else if (chCS == ',' && pszCS == nullptr &&
354 1623 : isspace(static_cast<unsigned char>(*pszStr)))
355 : {
356 : // In theory, the coordinates inside a coordinate tuple
357 : // should be separated by a comma. However it has been found
358 : // in the wild that the coordinates are in rare cases
359 : // separated by a space, and the tuples by a comma. See:
360 : // https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample
361 : // or
362 : // http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW
363 5 : chCS = ' ';
364 5 : chTS = ',';
365 : }
366 :
367 1739 : pszStr++;
368 :
369 1739 : if (iter == 1)
370 : {
371 1077 : if (chDecimal == '.')
372 1075 : dfY = OGRFastAtof(pszStr);
373 : else
374 2 : dfY = CPLAtofDelim(pszStr, chDecimal);
375 : }
376 13615 : while (*pszStr != '\0' && *pszStr != chCS && *pszStr != chTS &&
377 11878 : !isspace(static_cast<unsigned char>(*pszStr)))
378 11876 : pszStr++;
379 :
380 1739 : double dfZ = 0.0;
381 1739 : if (*pszStr == chCS)
382 : {
383 62 : pszStr++;
384 62 : if (iter == 1)
385 : {
386 34 : if (chDecimal == '.')
387 33 : dfZ = OGRFastAtof(pszStr);
388 : else
389 1 : dfZ = CPLAtofDelim(pszStr, chDecimal);
390 : }
391 62 : nDimension = 3;
392 286 : while (*pszStr != '\0' && *pszStr != chCS &&
393 588 : *pszStr != chTS &&
394 250 : !isspace(static_cast<unsigned char>(*pszStr)))
395 240 : pszStr++;
396 : }
397 :
398 1739 : if (*pszStr == chTS)
399 : {
400 1121 : pszStr++;
401 : }
402 :
403 1935 : while (isspace(static_cast<unsigned char>(*pszStr)))
404 196 : pszStr++;
405 :
406 1739 : if (iter == 1)
407 : {
408 1077 : if (poCurve)
409 : {
410 662 : if (nDimension == 3)
411 28 : poCurve->setPoint(iCoord, dfX, dfY, dfZ);
412 : else
413 634 : poCurve->setPoint(iCoord, dfX, dfY);
414 : }
415 415 : else if (!AddPoint(poGeometry, dfX, dfY, dfZ, nDimension))
416 1 : return false;
417 : }
418 :
419 1738 : iCoord++;
420 : }
421 :
422 619 : if (poCurve && iter == 0)
423 : {
424 103 : poCurve->setNumPoints(iCoord);
425 : }
426 : }
427 :
428 516 : return iCoord > 0;
429 : }
430 :
431 : /* -------------------------------------------------------------------- */
432 : /* Is this a "pos"? GML 3 construct. */
433 : /* Parse if it exist a series of pos elements (this would allow */
434 : /* the correct parsing of gml3.1.1 geometries such as linestring */
435 : /* defined with pos elements. */
436 : /* -------------------------------------------------------------------- */
437 2937 : bool bHasFoundPosElement = false;
438 7687 : for (const CPLXMLNode *psPos = psGeomNode->psChild; psPos != nullptr;
439 4750 : psPos = psPos->psNext)
440 : {
441 4753 : if (psPos->eType != CXT_Element)
442 3856 : continue;
443 :
444 3154 : const char *pszSubElement = BareGMLElement(psPos->pszValue);
445 :
446 3154 : if (EQUAL(pszSubElement, "pointProperty"))
447 : {
448 2 : for (const CPLXMLNode *psPointPropertyIter = psPos->psChild;
449 4 : psPointPropertyIter != nullptr;
450 2 : psPointPropertyIter = psPointPropertyIter->psNext)
451 : {
452 2 : if (psPointPropertyIter->eType != CXT_Element)
453 0 : continue;
454 :
455 : const char *pszBareElement =
456 2 : BareGMLElement(psPointPropertyIter->pszValue);
457 2 : if (EQUAL(pszBareElement, "Point") ||
458 0 : EQUAL(pszBareElement, "ElevatedPoint"))
459 : {
460 2 : OGRPoint oPoint;
461 2 : if (ParseGMLCoordinates(psPointPropertyIter, &oPoint,
462 : nSRSDimension))
463 : {
464 2 : const bool bSuccess = AddPoint(
465 : poGeometry, oPoint.getX(), oPoint.getY(),
466 : oPoint.getZ(), oPoint.getCoordinateDimension());
467 2 : if (bSuccess)
468 2 : bHasFoundPosElement = true;
469 : else
470 0 : return false;
471 : }
472 : }
473 : }
474 :
475 2 : if (psPos->psChild && psPos->psChild->eType == CXT_Attribute &&
476 0 : psPos->psChild->psNext == nullptr &&
477 0 : strcmp(psPos->psChild->pszValue, "xlink:href") == 0)
478 : {
479 0 : CPLError(CE_Warning, CPLE_AppDefined,
480 : "Cannot resolve xlink:href='%s'. "
481 : "Try setting GML_SKIP_RESOLVE_ELEMS=NONE",
482 0 : psPos->psChild->psChild->pszValue);
483 : }
484 :
485 2 : continue;
486 : }
487 :
488 3152 : if (!EQUAL(pszSubElement, "pos"))
489 2255 : continue;
490 :
491 897 : const char *pszPos = GetElementText(psPos);
492 897 : if (pszPos == nullptr)
493 : {
494 1 : poGeometry->empty();
495 1 : return true;
496 : }
497 :
498 896 : const char *pszCur = pszPos;
499 896 : const char *pszX = GMLGetCoordTokenPos(pszCur, &pszCur);
500 896 : const char *pszY = (pszCur[0] != '\0')
501 896 : ? GMLGetCoordTokenPos(pszCur, &pszCur)
502 896 : : nullptr;
503 896 : const char *pszZ = (pszCur[0] != '\0')
504 896 : ? GMLGetCoordTokenPos(pszCur, &pszCur)
505 896 : : nullptr;
506 :
507 896 : if (pszY == nullptr)
508 : {
509 1 : CPLError(CE_Failure, CPLE_AppDefined,
510 : "Did not get 2+ values in <gml:pos>%s</gml:pos> tuple.",
511 : pszPos);
512 1 : return false;
513 : }
514 :
515 895 : const double dfX = OGRFastAtof(pszX);
516 895 : const double dfY = OGRFastAtof(pszY);
517 895 : const double dfZ = (pszZ != nullptr) ? OGRFastAtof(pszZ) : 0.0;
518 : const bool bSuccess =
519 895 : AddPoint(poGeometry, dfX, dfY, dfZ, (pszZ != nullptr) ? 3 : 2);
520 :
521 895 : if (bSuccess)
522 894 : bHasFoundPosElement = true;
523 : else
524 1 : return false;
525 : }
526 :
527 2934 : if (bHasFoundPosElement)
528 721 : return true;
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* Is this a "posList"? GML 3 construct (SF profile). */
532 : /* -------------------------------------------------------------------- */
533 2213 : const CPLXMLNode *psPosList = FindBareXMLChild(psGeomNode, "posList");
534 :
535 2213 : if (psPosList != nullptr)
536 : {
537 2187 : int nDimension = 2;
538 :
539 : // Try to detect the presence of an srsDimension attribute
540 : // This attribute is only available for gml3.1.1 but not
541 : // available for gml3.1 SF.
542 : const char *pszSRSDimension =
543 2187 : CPLGetXMLValue(psPosList, "srsDimension", nullptr);
544 : // If not found at the posList level, try on the enclosing element.
545 2187 : if (pszSRSDimension == nullptr)
546 : pszSRSDimension =
547 810 : CPLGetXMLValue(psGeomNode, "srsDimension", nullptr);
548 2187 : if (pszSRSDimension != nullptr)
549 1378 : nDimension = atoi(pszSRSDimension);
550 809 : else if (nSRSDimension != 0)
551 : // Or use one coming from a still higher level element (#5606).
552 429 : nDimension = nSRSDimension;
553 :
554 2187 : if (nDimension != 2 && nDimension != 3)
555 : {
556 1 : CPLError(CE_Failure, CPLE_AppDefined,
557 : "srsDimension = %d not supported", nDimension);
558 1 : return false;
559 : }
560 :
561 2186 : const char *pszPosList = GetElementText(psPosList);
562 2186 : if (pszPosList == nullptr)
563 : {
564 3 : poGeometry->empty();
565 3 : return true;
566 : }
567 :
568 2183 : bool bSuccess = false;
569 2183 : const char *pszCur = pszPosList;
570 : while (true)
571 : {
572 194511 : const char *pszX = GMLGetCoordTokenPos(pszCur, &pszCur);
573 194511 : if (pszX == nullptr && bSuccess)
574 2180 : break;
575 192331 : const char *pszY = (pszCur[0] != '\0')
576 192331 : ? GMLGetCoordTokenPos(pszCur, &pszCur)
577 192331 : : nullptr;
578 20707 : const char *pszZ = (nDimension == 3 && pszCur[0] != '\0')
579 213038 : ? GMLGetCoordTokenPos(pszCur, &pszCur)
580 192331 : : nullptr;
581 :
582 192331 : if (pszY == nullptr || (nDimension == 3 && pszZ == nullptr))
583 : {
584 3 : CPLError(CE_Failure, CPLE_AppDefined,
585 : "Did not get at least %d values or invalid number of "
586 : "set of coordinates <gml:posList>%s</gml:posList>",
587 : nDimension, pszPosList);
588 3 : return false;
589 : }
590 :
591 192328 : double dfX = OGRFastAtof(pszX);
592 192328 : double dfY = OGRFastAtof(pszY);
593 192328 : double dfZ = (pszZ != nullptr) ? OGRFastAtof(pszZ) : 0.0;
594 192328 : bSuccess = AddPoint(poGeometry, dfX, dfY, dfZ, nDimension);
595 :
596 192328 : if (!bSuccess || pszCur == nullptr)
597 : break;
598 192328 : }
599 :
600 2180 : return bSuccess;
601 : }
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Handle form with a list of <coord> items each with an <X>, */
605 : /* and <Y> element. */
606 : /* -------------------------------------------------------------------- */
607 26 : int iCoord = 0;
608 26 : for (const CPLXMLNode *psCoordNode = psGeomNode->psChild;
609 43 : psCoordNode != nullptr; psCoordNode = psCoordNode->psNext)
610 : {
611 38 : if (psCoordNode->eType != CXT_Element ||
612 18 : !EQUAL(BareGMLElement(psCoordNode->pszValue), "coord"))
613 15 : continue;
614 :
615 5 : const CPLXMLNode *psXNode = FindBareXMLChild(psCoordNode, "X");
616 5 : const CPLXMLNode *psYNode = FindBareXMLChild(psCoordNode, "Y");
617 5 : const CPLXMLNode *psZNode = FindBareXMLChild(psCoordNode, "Z");
618 :
619 7 : if (psXNode == nullptr || psYNode == nullptr ||
620 5 : GetElementText(psXNode) == nullptr ||
621 11 : GetElementText(psYNode) == nullptr ||
622 0 : (psZNode != nullptr && GetElementText(psZNode) == nullptr))
623 : {
624 3 : CPLError(CE_Failure, CPLE_AppDefined,
625 : "Corrupt <coord> element, missing <X> or <Y> element?");
626 3 : return false;
627 : }
628 :
629 2 : double dfX = OGRFastAtof(GetElementText(psXNode));
630 2 : double dfY = OGRFastAtof(GetElementText(psYNode));
631 :
632 2 : int nDimension = 2;
633 2 : double dfZ = 0.0;
634 2 : if (psZNode != nullptr && GetElementText(psZNode) != nullptr)
635 : {
636 0 : dfZ = OGRFastAtof(GetElementText(psZNode));
637 0 : nDimension = 3;
638 : }
639 :
640 2 : if (!AddPoint(poGeometry, dfX, dfY, dfZ, nDimension))
641 0 : return false;
642 :
643 2 : iCoord++;
644 : }
645 :
646 23 : return iCoord > 0;
647 : }
648 :
649 : #ifdef HAVE_GEOS
650 : /************************************************************************/
651 : /* GML2FaceExtRing() */
652 : /* */
653 : /* Identifies the "good" Polygon within the collection returned */
654 : /* by GEOSPolygonize() */
655 : /* short rationale: GEOSPolygonize() will possibly return a */
656 : /* collection of many Polygons; only one is the "good" one, */
657 : /* (including both exterior- and interior-rings) */
658 : /* any other simply represents a single "hole", and should be */
659 : /* consequently ignored at all. */
660 : /************************************************************************/
661 :
662 114 : static std::unique_ptr<OGRPolygon> GML2FaceExtRing(const OGRGeometry *poGeom)
663 : {
664 : const OGRGeometryCollection *poColl =
665 114 : dynamic_cast<const OGRGeometryCollection *>(poGeom);
666 114 : if (poColl == nullptr)
667 : {
668 0 : CPLError(CE_Fatal, CPLE_AppDefined,
669 : "dynamic_cast failed. Expected OGRGeometryCollection.");
670 0 : return nullptr;
671 : }
672 :
673 114 : const OGRPolygon *poPolygonExterior = nullptr;
674 114 : const OGRPolygon *poPolygonInterior = nullptr;
675 114 : int iExterior = 0;
676 114 : int iInterior = 0;
677 :
678 256 : for (const auto *poChild : *poColl)
679 : {
680 : // A collection of Polygons is expected to be found.
681 142 : if (wkbFlatten(poChild->getGeometryType()) == wkbPolygon)
682 : {
683 142 : const OGRPolygon *poPoly = poChild->toPolygon();
684 142 : if (poPoly->getNumInteriorRings() > 0)
685 : {
686 20 : poPolygonExterior = poPoly;
687 20 : iExterior++;
688 : }
689 : else
690 : {
691 122 : poPolygonInterior = poPoly;
692 122 : iInterior++;
693 : }
694 : }
695 : else
696 : {
697 0 : return nullptr;
698 : }
699 : }
700 :
701 114 : if (poPolygonInterior && iExterior == 0 && iInterior == 1)
702 : {
703 : // There is a single Polygon within the collection.
704 94 : return std::unique_ptr<OGRPolygon>(poPolygonInterior->clone());
705 : }
706 40 : else if (poPolygonExterior && iExterior == 1 &&
707 20 : iInterior == poColl->getNumGeometries() - 1)
708 : {
709 : // Return the unique Polygon containing holes.
710 20 : return std::unique_ptr<OGRPolygon>(poPolygonExterior->clone());
711 : }
712 :
713 0 : return nullptr;
714 : }
715 : #endif
716 :
717 : /************************************************************************/
718 : /* GML2OGRGeometry_AddToCompositeCurve() */
719 : /************************************************************************/
720 :
721 : static bool
722 53 : GML2OGRGeometry_AddToCompositeCurve(OGRCompoundCurve *poCC,
723 : std::unique_ptr<OGRGeometry> poGeom,
724 : bool &bChildrenAreAllLineString)
725 : {
726 53 : if (poGeom == nullptr || !OGR_GT_IsCurve(poGeom->getGeometryType()))
727 : {
728 0 : CPLError(CE_Failure, CPLE_AppDefined,
729 : "CompositeCurve: Got %s geometry as Member instead of a "
730 : "curve.",
731 0 : poGeom ? poGeom->getGeometryName() : "NULL");
732 0 : return false;
733 : }
734 :
735 : // Crazy but allowed by GML: composite in composite.
736 53 : if (wkbFlatten(poGeom->getGeometryType()) == wkbCompoundCurve)
737 : {
738 : auto poCCChild = std::unique_ptr<OGRCompoundCurve>(
739 2 : poGeom.release()->toCompoundCurve());
740 5 : while (poCCChild->getNumCurves() != 0)
741 : {
742 4 : auto poCurve = std::unique_ptr<OGRCurve>(poCCChild->stealCurve(0));
743 4 : if (wkbFlatten(poCurve->getGeometryType()) != wkbLineString)
744 2 : bChildrenAreAllLineString = false;
745 4 : if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE)
746 : {
747 1 : return false;
748 : }
749 : }
750 : }
751 : else
752 : {
753 51 : if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
754 14 : bChildrenAreAllLineString = false;
755 :
756 51 : auto poCurve = std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
757 51 : if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE)
758 : {
759 0 : return false;
760 : }
761 : }
762 52 : return true;
763 : }
764 :
765 : /************************************************************************/
766 : /* GML2OGRGeometry_AddToMultiSurface() */
767 : /************************************************************************/
768 :
769 93 : static bool GML2OGRGeometry_AddToMultiSurface(
770 : OGRMultiSurface *poMS, std::unique_ptr<OGRGeometry> poGeom,
771 : const char *pszMemberElement, bool &bChildrenAreAllPolygons)
772 : {
773 93 : if (poGeom == nullptr)
774 : {
775 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s", pszMemberElement);
776 1 : return false;
777 : }
778 :
779 92 : OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
780 92 : if (eType == wkbPolygon || eType == wkbCurvePolygon)
781 : {
782 89 : if (eType != wkbPolygon)
783 11 : bChildrenAreAllPolygons = false;
784 :
785 89 : if (poMS->addGeometry(std::move(poGeom)) != OGRERR_NONE)
786 : {
787 0 : return false;
788 : }
789 : }
790 3 : else if (eType == wkbMultiPolygon || eType == wkbMultiSurface)
791 : {
792 2 : OGRMultiSurface *poMS2 = poGeom->toMultiSurface();
793 6 : for (int i = 0; i < poMS2->getNumGeometries(); i++)
794 : {
795 4 : if (wkbFlatten(poMS2->getGeometryRef(i)->getGeometryType()) !=
796 : wkbPolygon)
797 0 : bChildrenAreAllPolygons = false;
798 :
799 4 : if (poMS->addGeometry(poMS2->getGeometryRef(i)) != OGRERR_NONE)
800 : {
801 0 : return false;
802 : }
803 2 : }
804 : }
805 : else
806 : {
807 1 : CPLError(CE_Failure, CPLE_AppDefined, "Got %s geometry as %s.",
808 1 : poGeom->getGeometryName(), pszMemberElement);
809 1 : return false;
810 : }
811 91 : return true;
812 : }
813 :
814 : /************************************************************************/
815 : /* GetUOMInMetre() */
816 : /************************************************************************/
817 :
818 36 : static double GetUOMInMetre(const char *pszUnits, const char *pszAttribute,
819 : const char *pszId)
820 : {
821 36 : if (!pszUnits || EQUAL(pszUnits, "m"))
822 7 : return 1.0;
823 :
824 29 : if (EQUAL(pszUnits, "km"))
825 3 : return 1000.0;
826 :
827 26 : if (EQUAL(pszUnits, "nm") || EQUAL(pszUnits, "[nmi_i]"))
828 20 : return CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV);
829 :
830 6 : if (EQUAL(pszUnits, "mi"))
831 0 : return CPLAtof(SRS_UL_INTL_STAT_MILE_CONV);
832 :
833 6 : if (EQUAL(pszUnits, "ft"))
834 1 : return CPLAtof(SRS_UL_INTL_FOOT_CONV);
835 :
836 5 : if (pszId)
837 : {
838 1 : CPLError(CE_Warning, CPLE_AppDefined,
839 : "GML geometry id='%s': Unhandled distance unit '%s' in "
840 : "attribute '%s'",
841 : pszId, pszUnits, pszAttribute);
842 : }
843 : else
844 : {
845 4 : CPLError(CE_Warning, CPLE_AppDefined,
846 : "Unhandled distance unit '%s' in attribute '%s'", pszUnits,
847 : pszAttribute);
848 : }
849 5 : return -1;
850 : }
851 :
852 : /************************************************************************/
853 : /* GetSemiMajor() */
854 : /************************************************************************/
855 :
856 28 : static double GetSemiMajor(const OGRSpatialReference *poSRS)
857 : {
858 28 : double dfSemiMajor = poSRS->GetSemiMajor();
859 : // Standardize on OGR_GREATCIRCLE_DEFAULT_RADIUS for Earth ellipsoids.
860 28 : if (std::fabs(dfSemiMajor - OGR_GREATCIRCLE_DEFAULT_RADIUS) <
861 : 0.05 * OGR_GREATCIRCLE_DEFAULT_RADIUS)
862 28 : dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
863 28 : return dfSemiMajor;
864 : }
865 :
866 : /************************************************************************/
867 : /* GML2OGRGeometry_XMLNode() */
868 : /* */
869 : /* Translates the passed XMLnode and its children into an */
870 : /* OGRGeometry. This is used recursively for geometry */
871 : /* collections. */
872 : /************************************************************************/
873 :
874 : static std::unique_ptr<OGRGeometry> GML2OGRGeometry_XMLNode_Internal(
875 : const CPLXMLNode *psNode, const char *pszId,
876 : int nPseudoBoolGetSecondaryGeometryOption, int nRecLevel, int nSRSDimension,
877 : const char *pszSRSName,
878 : lru11::Cache<std::string, std::shared_ptr<OGRSpatialReference>> &oSRSCache,
879 : bool bIgnoreGSG = false, bool bOrientation = true,
880 : bool bFaceHoleNegative = false);
881 :
882 2626 : OGRGeometry *GML2OGRGeometry_XMLNode(const CPLXMLNode *psNode,
883 : int nPseudoBoolGetSecondaryGeometryOption,
884 : int nRecLevel, int nSRSDimension,
885 : bool bIgnoreGSG, bool bOrientation,
886 : bool bFaceHoleNegative, const char *pszId)
887 :
888 : {
889 2626 : lru11::Cache<std::string, std::shared_ptr<OGRSpatialReference>> oSRSCache;
890 5252 : return GML2OGRGeometry_XMLNode_Internal(
891 : psNode, pszId, nPseudoBoolGetSecondaryGeometryOption, nRecLevel,
892 : nSRSDimension, nullptr, oSRSCache, bIgnoreGSG, bOrientation,
893 : bFaceHoleNegative)
894 5252 : .release();
895 : }
896 :
897 : static void ReportError(const char *pszId, CPLErr eErr, const char *fmt, ...)
898 : CPL_PRINT_FUNC_FORMAT(3, 4);
899 :
900 114 : static void ReportError(const char *pszId, CPLErr eErr, const char *fmt, ...)
901 : {
902 : va_list ap;
903 114 : va_start(ap, fmt);
904 114 : if (pszId)
905 : {
906 0 : std::string osMsg("GML geometry id='");
907 0 : osMsg += pszId;
908 0 : osMsg += "': ";
909 0 : osMsg += CPLString().vPrintf(fmt, ap);
910 0 : CPLError(eErr, CPLE_AppDefined, "%s", osMsg.c_str());
911 : }
912 : else
913 : {
914 114 : CPLErrorV(eErr, CPLE_AppDefined, fmt, ap);
915 : }
916 114 : va_end(ap);
917 114 : }
918 :
919 8014 : static std::unique_ptr<OGRGeometry> GML2OGRGeometry_XMLNode_Internal(
920 : const CPLXMLNode *psNode, const char *pszId,
921 : int nPseudoBoolGetSecondaryGeometryOption, int nRecLevel, int nSRSDimension,
922 : const char *pszSRSName,
923 : lru11::Cache<std::string, std::shared_ptr<OGRSpatialReference>> &oSRSCache,
924 : bool bIgnoreGSG, bool bOrientation, bool bFaceHoleNegative)
925 : {
926 : // constexpr bool bCastToLinearTypeIfPossible = true; // Hard-coded for
927 : // now.
928 :
929 : // We need this nRecLevel == 0 check, otherwise this could result in
930 : // multiple revisit of the same node, and exponential complexity.
931 8014 : if (nRecLevel == 0 && psNode != nullptr &&
932 2626 : strcmp(psNode->pszValue, "?xml") == 0)
933 4 : psNode = psNode->psNext;
934 8016 : while (psNode != nullptr && psNode->eType == CXT_Comment)
935 2 : psNode = psNode->psNext;
936 8014 : if (psNode == nullptr)
937 2 : return nullptr;
938 :
939 : const char *pszSRSDimension =
940 8012 : CPLGetXMLValue(psNode, "srsDimension", nullptr);
941 8012 : if (pszSRSDimension != nullptr)
942 621 : nSRSDimension = atoi(pszSRSDimension);
943 :
944 8012 : if (pszSRSName == nullptr)
945 6089 : pszSRSName = CPLGetXMLValue(psNode, "srsName", nullptr);
946 :
947 8012 : if (!pszId && nRecLevel == 0)
948 : {
949 2624 : pszId = CPLGetXMLValue(psNode, "gml:id", nullptr);
950 : }
951 :
952 8012 : const char *pszBaseGeometry = BareGMLElement(psNode->pszValue);
953 8012 : if (nPseudoBoolGetSecondaryGeometryOption < 0)
954 1093 : nPseudoBoolGetSecondaryGeometryOption =
955 1093 : CPLTestBool(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
956 : bool bGetSecondaryGeometry =
957 8012 : bIgnoreGSG ? false : CPL_TO_BOOL(nPseudoBoolGetSecondaryGeometryOption);
958 :
959 : #define ReportFailure(...) ReportError(pszId, CE_Failure, __VA_ARGS__)
960 :
961 : #define ReportWarning(...) ReportError(pszId, CE_Warning, __VA_ARGS__)
962 :
963 : // Arbitrary value, but certainly large enough for reasonable usages.
964 8012 : if (nRecLevel == 32)
965 : {
966 1 : ReportFailure(
967 : "Too many recursion levels (%d) while parsing GML geometry.",
968 : nRecLevel);
969 1 : return nullptr;
970 : }
971 :
972 8011 : if (bGetSecondaryGeometry)
973 0 : if (!(EQUAL(pszBaseGeometry, "directedEdge") ||
974 0 : EQUAL(pszBaseGeometry, "TopoCurve")))
975 0 : return nullptr;
976 :
977 : /* -------------------------------------------------------------------- */
978 : /* Polygon / PolygonPatch / Rectangle */
979 : /* -------------------------------------------------------------------- */
980 8011 : if (EQUAL(pszBaseGeometry, "Polygon") ||
981 7242 : EQUAL(pszBaseGeometry, "PolygonPatch") ||
982 7183 : EQUAL(pszBaseGeometry, "Rectangle"))
983 : {
984 : // Find outer ring.
985 830 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "outerBoundaryIs");
986 830 : if (psChild == nullptr)
987 747 : psChild = FindBareXMLChild(psNode, "exterior");
988 :
989 830 : psChild = GetChildElement(psChild);
990 830 : if (psChild == nullptr)
991 : {
992 : // <gml:Polygon/> is invalid GML2, but valid GML3, so be tolerant.
993 5 : return std::make_unique<OGRPolygon>();
994 : }
995 :
996 : // Translate outer ring and add to polygon.
997 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
998 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
999 1650 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
1000 825 : if (poGeom == nullptr)
1001 : {
1002 3 : ReportFailure("Invalid exterior ring");
1003 3 : return nullptr;
1004 : }
1005 :
1006 822 : if (!OGR_GT_IsCurve(poGeom->getGeometryType()))
1007 : {
1008 1 : ReportFailure("%s: Got %s geometry as outerBoundaryIs.",
1009 : pszBaseGeometry, poGeom->getGeometryName());
1010 1 : return nullptr;
1011 : }
1012 :
1013 1611 : if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1014 790 : !EQUAL(poGeom->getGeometryName(), "LINEARRING"))
1015 : {
1016 8 : OGRCurve *poCurve = poGeom.release()->toCurve();
1017 8 : auto poLinearRing = OGRCurve::CastToLinearRing(poCurve);
1018 8 : if (!poLinearRing)
1019 1 : return nullptr;
1020 7 : poGeom.reset(poLinearRing);
1021 : }
1022 :
1023 820 : std::unique_ptr<OGRCurvePolygon> poCP;
1024 820 : bool bIsPolygon = false;
1025 820 : assert(poGeom); // to please cppcheck
1026 820 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
1027 : {
1028 789 : poCP = std::make_unique<OGRPolygon>();
1029 789 : bIsPolygon = true;
1030 : }
1031 : else
1032 : {
1033 31 : poCP = std::make_unique<OGRCurvePolygon>();
1034 31 : bIsPolygon = false;
1035 : }
1036 :
1037 : {
1038 : auto poCurve =
1039 820 : std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
1040 820 : if (poCP->addRing(std::move(poCurve)) != OGRERR_NONE)
1041 : {
1042 0 : return nullptr;
1043 : }
1044 : }
1045 :
1046 : // Find all inner rings
1047 3189 : for (psChild = psNode->psChild; psChild != nullptr;
1048 2369 : psChild = psChild->psNext)
1049 : {
1050 3208 : if (psChild->eType == CXT_Element &&
1051 837 : (EQUAL(BareGMLElement(psChild->pszValue), "innerBoundaryIs") ||
1052 833 : EQUAL(BareGMLElement(psChild->pszValue), "interior")))
1053 : {
1054 17 : const CPLXMLNode *psInteriorChild = GetChildElement(psChild);
1055 0 : std::unique_ptr<OGRGeometry> poGeomInterior;
1056 17 : if (psInteriorChild != nullptr)
1057 32 : poGeomInterior = GML2OGRGeometry_XMLNode_Internal(
1058 : psInteriorChild, pszId,
1059 : nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
1060 16 : nSRSDimension, pszSRSName, oSRSCache);
1061 17 : if (poGeomInterior == nullptr)
1062 : {
1063 1 : ReportFailure("Invalid interior ring");
1064 1 : return nullptr;
1065 : }
1066 :
1067 16 : if (!OGR_GT_IsCurve(poGeomInterior->getGeometryType()))
1068 : {
1069 1 : ReportFailure("%s: Got %s geometry as innerBoundaryIs.",
1070 : pszBaseGeometry,
1071 : poGeomInterior->getGeometryName());
1072 1 : return nullptr;
1073 : }
1074 :
1075 15 : if (bIsPolygon)
1076 : {
1077 11 : if (!EQUAL(poGeomInterior->getGeometryName(), "LINEARRING"))
1078 : {
1079 1 : if (wkbFlatten(poGeomInterior->getGeometryType()) ==
1080 : wkbLineString)
1081 : {
1082 : OGRLineString *poLS =
1083 0 : poGeomInterior.release()->toLineString();
1084 : auto poLinearRing =
1085 0 : OGRCurve::CastToLinearRing(poLS);
1086 0 : if (!poLinearRing)
1087 0 : return nullptr;
1088 0 : poGeomInterior.reset(poLinearRing);
1089 : }
1090 : else
1091 : {
1092 : // Might fail if some rings are not closed.
1093 : // We used to be tolerant about that with Polygon.
1094 : // but we have become stricter with CurvePolygon.
1095 : auto poCPNew = std::unique_ptr<OGRCurvePolygon>(
1096 1 : OGRSurface::CastToCurvePolygon(poCP.release()));
1097 1 : if (!poCPNew)
1098 : {
1099 0 : return nullptr;
1100 : }
1101 1 : poCP = std::move(poCPNew);
1102 1 : bIsPolygon = false;
1103 : }
1104 : }
1105 : }
1106 : else
1107 : {
1108 4 : if (EQUAL(poGeomInterior->getGeometryName(), "LINEARRING"))
1109 : {
1110 2 : OGRCurve *poCurve = poGeomInterior.release()->toCurve();
1111 2 : poGeomInterior.reset(
1112 2 : OGRCurve::CastToLineString(poCurve));
1113 : }
1114 : }
1115 : auto poCurve = std::unique_ptr<OGRCurve>(
1116 15 : poGeomInterior.release()->toCurve());
1117 15 : if (poCP->addRing(std::move(poCurve)) != OGRERR_NONE)
1118 : {
1119 0 : return nullptr;
1120 : }
1121 : }
1122 : }
1123 :
1124 818 : return poCP;
1125 : }
1126 :
1127 : /* -------------------------------------------------------------------- */
1128 : /* Triangle */
1129 : /* -------------------------------------------------------------------- */
1130 :
1131 7181 : if (EQUAL(pszBaseGeometry, "Triangle"))
1132 : {
1133 : // Find outer ring.
1134 11 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "exterior");
1135 11 : if (!psChild)
1136 1 : return nullptr;
1137 :
1138 10 : psChild = GetChildElement(psChild);
1139 10 : if (psChild == nullptr)
1140 : {
1141 0 : ReportFailure("Empty Triangle");
1142 0 : return std::make_unique<OGRTriangle>();
1143 : }
1144 :
1145 : // Translate outer ring and add to Triangle.
1146 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
1147 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
1148 20 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
1149 10 : if (poGeom == nullptr)
1150 : {
1151 0 : ReportFailure("Invalid exterior ring");
1152 0 : return nullptr;
1153 : }
1154 :
1155 10 : if (!OGR_GT_IsCurve(poGeom->getGeometryType()))
1156 : {
1157 0 : ReportFailure("%s: Got %s geometry as outerBoundaryIs.",
1158 : pszBaseGeometry, poGeom->getGeometryName());
1159 0 : return nullptr;
1160 : }
1161 :
1162 20 : if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1163 10 : !EQUAL(poGeom->getGeometryName(), "LINEARRING"))
1164 : {
1165 1 : poGeom.reset(
1166 1 : OGRCurve::CastToLinearRing(poGeom.release()->toCurve()));
1167 : }
1168 :
1169 19 : if (poGeom == nullptr ||
1170 9 : !EQUAL(poGeom->getGeometryName(), "LINEARRING"))
1171 : {
1172 1 : return nullptr;
1173 : }
1174 :
1175 18 : auto poTriangle = std::make_unique<OGRTriangle>();
1176 18 : auto poCurve = std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
1177 9 : if (poTriangle->addRing(std::move(poCurve)) != OGRERR_NONE)
1178 : {
1179 0 : return nullptr;
1180 : }
1181 :
1182 9 : return poTriangle;
1183 : }
1184 :
1185 : /* -------------------------------------------------------------------- */
1186 : /* LinearRing */
1187 : /* -------------------------------------------------------------------- */
1188 7170 : if (EQUAL(pszBaseGeometry, "LinearRing"))
1189 : {
1190 1600 : auto poLinearRing = std::make_unique<OGRLinearRing>();
1191 :
1192 800 : if (!ParseGMLCoordinates(psNode, poLinearRing.get(), nSRSDimension))
1193 : {
1194 1 : return nullptr;
1195 : }
1196 :
1197 799 : return poLinearRing;
1198 : }
1199 :
1200 : const auto storeArcByCenterPointParameters =
1201 16 : [pszId, &oSRSCache](const CPLXMLNode *psChild, const char *l_pszSRSName,
1202 : bool &bIsApproximateArc,
1203 : double &dfLastCurveApproximateArcRadius,
1204 : bool &bLastCurveWasApproximateArcInvertedAxisOrder,
1205 32 : double &dfSemiMajor)
1206 : {
1207 16 : const CPLXMLNode *psRadius = FindBareXMLChild(psChild, "radius");
1208 16 : if (psRadius && psRadius->eType == CXT_Element)
1209 : {
1210 16 : const char *pszUnits = CPLGetXMLValue(psRadius, "uom", nullptr);
1211 16 : const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId);
1212 : const double dfRadiusRaw =
1213 16 : CPLAtof(CPLGetXMLValue(psRadius, nullptr, "0"));
1214 16 : const double dfRadius =
1215 16 : dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw;
1216 16 : bool bSRSUnitIsDegree = false;
1217 16 : bool bInvertedAxisOrder = false;
1218 16 : if (l_pszSRSName != nullptr)
1219 : {
1220 16 : std::shared_ptr<OGRSpatialReference> poSRS;
1221 16 : if (!oSRSCache.tryGet(l_pszSRSName, poSRS))
1222 : {
1223 0 : poSRS = std::make_shared<OGRSpatialReference>();
1224 0 : if (poSRS->SetFromUserInput(
1225 : l_pszSRSName,
1226 : OGRSpatialReference::
1227 0 : SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
1228 : OGRERR_NONE)
1229 0 : poSRS.reset();
1230 0 : oSRSCache.insert(l_pszSRSName, poSRS);
1231 : }
1232 16 : if (poSRS && poSRS->IsGeographic())
1233 : {
1234 : bInvertedAxisOrder =
1235 14 : CPL_TO_BOOL(poSRS->EPSGTreatsAsLatLong());
1236 14 : dfSemiMajor = GetSemiMajor(poSRS.get());
1237 14 : bSRSUnitIsDegree = fabs(poSRS->GetAngularUnits(nullptr) -
1238 14 : CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
1239 : }
1240 : }
1241 16 : if (bSRSUnitIsDegree && dfUOMConv > 0)
1242 : {
1243 14 : bIsApproximateArc = true;
1244 14 : dfLastCurveApproximateArcRadius = dfRadius;
1245 14 : bLastCurveWasApproximateArcInvertedAxisOrder =
1246 : bInvertedAxisOrder;
1247 : }
1248 : }
1249 16 : };
1250 :
1251 : const auto connectArcByCenterPointToOtherSegments =
1252 46 : [](OGRGeometry *poGeom, OGRCompoundCurve *poCC,
1253 : const bool bIsApproximateArc, const bool bLastCurveWasApproximateArc,
1254 : const double dfLastCurveApproximateArcRadius,
1255 : const bool bLastCurveWasApproximateArcInvertedAxisOrder,
1256 : const double dfSemiMajor)
1257 : {
1258 46 : if (bIsApproximateArc)
1259 : {
1260 5 : if (poGeom->getGeometryType() == wkbLineString)
1261 : {
1262 : OGRCurve *poPreviousCurve =
1263 5 : poCC->getCurve(poCC->getNumCurves() - 1);
1264 5 : OGRLineString *poLS = poGeom->toLineString();
1265 10 : if (poPreviousCurve->getNumPoints() >= 2 &&
1266 5 : poLS->getNumPoints() >= 2)
1267 : {
1268 10 : OGRPoint p;
1269 10 : OGRPoint p2;
1270 5 : poPreviousCurve->EndPoint(&p);
1271 5 : poLS->StartPoint(&p2);
1272 5 : double dfDistance = 0.0;
1273 5 : if (bLastCurveWasApproximateArcInvertedAxisOrder)
1274 4 : dfDistance = OGR_GreatCircle_Distance(
1275 : p.getX(), p.getY(), p2.getX(), p2.getY(),
1276 : dfSemiMajor);
1277 : else
1278 1 : dfDistance = OGR_GreatCircle_Distance(
1279 : p.getY(), p.getX(), p2.getY(), p2.getX(),
1280 : dfSemiMajor);
1281 : // CPLDebug("OGR", "%f %f",
1282 : // dfDistance,
1283 : // dfLastCurveApproximateArcRadius
1284 : // / 10.0 );
1285 5 : if (dfDistance < dfLastCurveApproximateArcRadius / 5.0)
1286 : {
1287 5 : CPLDebug("OGR", "Moving approximate start of "
1288 : "ArcByCenterPoint to end of "
1289 : "previous curve");
1290 5 : poLS->setPoint(0, &p);
1291 : }
1292 : }
1293 : }
1294 : }
1295 41 : else if (bLastCurveWasApproximateArc)
1296 : {
1297 : OGRCurve *poPreviousCurve =
1298 5 : poCC->getCurve(poCC->getNumCurves() - 1);
1299 5 : if (poPreviousCurve->getGeometryType() == wkbLineString)
1300 : {
1301 5 : OGRLineString *poLS = poPreviousCurve->toLineString();
1302 5 : OGRCurve *poAsCurve = poGeom->toCurve();
1303 :
1304 5 : if (poLS->getNumPoints() >= 2 && poAsCurve->getNumPoints() >= 2)
1305 : {
1306 10 : OGRPoint p;
1307 10 : OGRPoint p2;
1308 5 : poAsCurve->StartPoint(&p);
1309 5 : poLS->EndPoint(&p2);
1310 5 : double dfDistance = 0.0;
1311 5 : if (bLastCurveWasApproximateArcInvertedAxisOrder)
1312 4 : dfDistance = OGR_GreatCircle_Distance(
1313 : p.getX(), p.getY(), p2.getX(), p2.getY(),
1314 : dfSemiMajor);
1315 : else
1316 1 : dfDistance = OGR_GreatCircle_Distance(
1317 : p.getY(), p.getX(), p2.getY(), p2.getX(),
1318 : dfSemiMajor);
1319 : // CPLDebug(
1320 : // "OGR", "%f %f",
1321 : // dfDistance,
1322 : // dfLastCurveApproximateArcRadius / 10.0 );
1323 :
1324 : // "A-311 WHEELER AFB OAHU, HI.xml" needs more
1325 : // than 10%.
1326 5 : if (dfDistance < dfLastCurveApproximateArcRadius / 5.0)
1327 : {
1328 5 : CPLDebug("OGR", "Moving approximate end of last "
1329 : "ArcByCenterPoint to start of the "
1330 : "current curve");
1331 5 : poLS->setPoint(poLS->getNumPoints() - 1, &p);
1332 : }
1333 : }
1334 : }
1335 : }
1336 46 : };
1337 :
1338 : /* -------------------------------------------------------------------- */
1339 : /* Ring GML3 */
1340 : /* -------------------------------------------------------------------- */
1341 6370 : if (EQUAL(pszBaseGeometry, "Ring"))
1342 : {
1343 49 : std::unique_ptr<OGRCurve> poRing;
1344 49 : std::unique_ptr<OGRCompoundCurve> poCC;
1345 49 : bool bChildrenAreAllLineString = true;
1346 :
1347 49 : bool bLastCurveWasApproximateArc = false;
1348 49 : bool bLastCurveWasApproximateArcInvertedAxisOrder = false;
1349 49 : double dfLastCurveApproximateArcRadius = 0.0;
1350 :
1351 49 : bool bIsFirstChild = true;
1352 49 : bool bFirstChildIsApproximateArc = false;
1353 49 : double dfFirstChildApproximateArcRadius = 0.0;
1354 49 : bool bFirstChildWasApproximateArcInvertedAxisOrder = false;
1355 :
1356 49 : double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
1357 :
1358 117 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
1359 68 : psChild = psChild->psNext)
1360 : {
1361 142 : if (psChild->eType == CXT_Element &&
1362 71 : EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
1363 : {
1364 70 : const CPLXMLNode *psCurveChild = GetChildElement(psChild);
1365 0 : std::unique_ptr<OGRGeometry> poGeom;
1366 70 : if (psCurveChild != nullptr)
1367 : {
1368 138 : poGeom = GML2OGRGeometry_XMLNode_Internal(
1369 : psCurveChild, pszId,
1370 : nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
1371 69 : nSRSDimension, pszSRSName, oSRSCache);
1372 : }
1373 : else
1374 : {
1375 1 : if (psChild->psChild &&
1376 0 : psChild->psChild->eType == CXT_Attribute &&
1377 0 : psChild->psChild->psNext == nullptr &&
1378 0 : strcmp(psChild->psChild->pszValue, "xlink:href") == 0)
1379 : {
1380 0 : ReportWarning("Cannot resolve xlink:href='%s'. "
1381 : "Try setting GML_SKIP_RESOLVE_ELEMS=NONE",
1382 : psChild->psChild->psChild->pszValue);
1383 : }
1384 1 : return nullptr;
1385 : }
1386 :
1387 : // Try to join multiline string to one linestring.
1388 137 : if (poGeom &&
1389 137 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1390 : {
1391 0 : poGeom.reset(OGRGeometryFactory::forceToLineString(
1392 : poGeom.release(), false));
1393 : }
1394 :
1395 137 : if (poGeom == nullptr ||
1396 68 : !OGR_GT_IsCurve(poGeom->getGeometryType()))
1397 : {
1398 2 : return nullptr;
1399 : }
1400 :
1401 67 : if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
1402 32 : bChildrenAreAllLineString = false;
1403 :
1404 : // Ad-hoc logic to handle nicely connecting ArcByCenterPoint
1405 : // with consecutive curves, as found in some AIXM files.
1406 67 : bool bIsApproximateArc = false;
1407 : const CPLXMLNode *psChild2, *psChild3;
1408 67 : if (strcmp(BareGMLElement(psCurveChild->pszValue), "Curve") ==
1409 63 : 0 &&
1410 63 : (psChild2 = GetChildElement(psCurveChild)) != nullptr &&
1411 63 : strcmp(BareGMLElement(psChild2->pszValue), "segments") ==
1412 63 : 0 &&
1413 193 : (psChild3 = GetChildElement(psChild2)) != nullptr &&
1414 63 : strcmp(BareGMLElement(psChild3->pszValue),
1415 : "ArcByCenterPoint") == 0)
1416 : {
1417 2 : storeArcByCenterPointParameters(
1418 : psChild3, pszSRSName, bIsApproximateArc,
1419 : dfLastCurveApproximateArcRadius,
1420 : bLastCurveWasApproximateArcInvertedAxisOrder,
1421 : dfSemiMajor);
1422 2 : if (bIsFirstChild && bIsApproximateArc)
1423 : {
1424 1 : bFirstChildIsApproximateArc = true;
1425 1 : dfFirstChildApproximateArcRadius =
1426 : dfLastCurveApproximateArcRadius;
1427 1 : bFirstChildWasApproximateArcInvertedAxisOrder =
1428 : bLastCurveWasApproximateArcInvertedAxisOrder;
1429 : }
1430 1 : else if (psChild3->psNext)
1431 : {
1432 0 : bIsApproximateArc = false;
1433 : }
1434 : }
1435 67 : bIsFirstChild = false;
1436 :
1437 67 : if (poCC == nullptr && poRing == nullptr)
1438 : {
1439 44 : poRing.reset(poGeom.release()->toCurve());
1440 : }
1441 : else
1442 : {
1443 23 : if (poCC == nullptr)
1444 : {
1445 6 : poCC = std::make_unique<OGRCompoundCurve>();
1446 6 : bool bIgnored = false;
1447 6 : if (!GML2OGRGeometry_AddToCompositeCurve(
1448 6 : poCC.get(), std::move(poRing), bIgnored))
1449 : {
1450 0 : return nullptr;
1451 : }
1452 6 : poRing.reset();
1453 : }
1454 :
1455 23 : connectArcByCenterPointToOtherSegments(
1456 : poGeom.get(), poCC.get(), bIsApproximateArc,
1457 : bLastCurveWasApproximateArc,
1458 : dfLastCurveApproximateArcRadius,
1459 : bLastCurveWasApproximateArcInvertedAxisOrder,
1460 : dfSemiMajor);
1461 :
1462 : auto poCurve =
1463 23 : std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
1464 :
1465 23 : bool bIgnored = false;
1466 23 : if (!GML2OGRGeometry_AddToCompositeCurve(
1467 23 : poCC.get(), std::move(poCurve), bIgnored))
1468 : {
1469 0 : return nullptr;
1470 : }
1471 : }
1472 :
1473 67 : bLastCurveWasApproximateArc = bIsApproximateArc;
1474 : }
1475 : }
1476 :
1477 : /* Detect if the last object in the following hierarchy is a
1478 : ArcByCenterPoint <gml:Ring> <gml:curveMember> (may be repeated)
1479 : <gml:Curve>
1480 : <gml:segments>
1481 : ....
1482 : <gml:ArcByCenterPoint ... />
1483 : </gml:segments>
1484 : </gml:Curve>
1485 : </gml:curveMember>
1486 : </gml:Ring>
1487 : */
1488 46 : bool bLastChildIsApproximateArc = false;
1489 114 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
1490 68 : psChild = psChild->psNext)
1491 : {
1492 136 : if (psChild->eType == CXT_Element &&
1493 68 : EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
1494 : {
1495 67 : const CPLXMLNode *psCurveMemberChild = GetChildElement(psChild);
1496 134 : if (psCurveMemberChild &&
1497 134 : psCurveMemberChild->eType == CXT_Element &&
1498 67 : EQUAL(BareGMLElement(psCurveMemberChild->pszValue),
1499 : "Curve"))
1500 : {
1501 : const CPLXMLNode *psCurveChild =
1502 63 : GetChildElement(psCurveMemberChild);
1503 126 : if (psCurveChild && psCurveChild->eType == CXT_Element &&
1504 63 : EQUAL(BareGMLElement(psCurveChild->pszValue),
1505 : "segments"))
1506 : {
1507 63 : for (const CPLXMLNode *psChild2 = psCurveChild->psChild;
1508 143 : psChild2 != nullptr; psChild2 = psChild2->psNext)
1509 : {
1510 160 : if (psChild2->eType == CXT_Element &&
1511 80 : EQUAL(BareGMLElement(psChild2->pszValue),
1512 : "ArcByCenterPoint"))
1513 : {
1514 7 : storeArcByCenterPointParameters(
1515 : psChild2, pszSRSName,
1516 : bLastChildIsApproximateArc,
1517 : dfLastCurveApproximateArcRadius,
1518 : bLastCurveWasApproximateArcInvertedAxisOrder,
1519 : dfSemiMajor);
1520 : }
1521 : else
1522 : {
1523 73 : bLastChildIsApproximateArc = false;
1524 : }
1525 : }
1526 : }
1527 : else
1528 : {
1529 0 : bLastChildIsApproximateArc = false;
1530 : }
1531 : }
1532 : else
1533 : {
1534 4 : bLastChildIsApproximateArc = false;
1535 : }
1536 : }
1537 : else
1538 : {
1539 1 : bLastChildIsApproximateArc = false;
1540 : }
1541 : }
1542 :
1543 46 : if (poRing)
1544 : {
1545 76 : if (poRing->getNumPoints() >= 2 && bFirstChildIsApproximateArc &&
1546 77 : !poRing->get_IsClosed() &&
1547 1 : wkbFlatten(poRing->getGeometryType()) == wkbLineString)
1548 : {
1549 1 : OGRLineString *poLS = poRing->toLineString();
1550 :
1551 2 : OGRPoint p;
1552 2 : OGRPoint p2;
1553 1 : poLS->StartPoint(&p);
1554 1 : poLS->EndPoint(&p2);
1555 1 : double dfDistance = 0.0;
1556 1 : if (bFirstChildWasApproximateArcInvertedAxisOrder)
1557 1 : dfDistance = OGR_GreatCircle_Distance(
1558 : p.getX(), p.getY(), p2.getX(), p2.getY(), dfSemiMajor);
1559 : else
1560 0 : dfDistance = OGR_GreatCircle_Distance(
1561 : p.getY(), p.getX(), p2.getY(), p2.getX(), dfSemiMajor);
1562 1 : if (dfDistance < dfFirstChildApproximateArcRadius / 5.0)
1563 : {
1564 1 : CPLDebug("OGR", "Moving approximate start of "
1565 : "ArcByCenterPoint to end of "
1566 : "curve");
1567 1 : poLS->setPoint(0, &p2);
1568 : }
1569 : }
1570 74 : else if (poRing->getNumPoints() >= 2 &&
1571 75 : bLastChildIsApproximateArc && !poRing->get_IsClosed() &&
1572 1 : wkbFlatten(poRing->getGeometryType()) == wkbLineString)
1573 : {
1574 1 : OGRLineString *poLS = poRing->toLineString();
1575 :
1576 2 : OGRPoint p;
1577 2 : OGRPoint p2;
1578 1 : poLS->StartPoint(&p);
1579 1 : poLS->EndPoint(&p2);
1580 1 : double dfDistance = 0.0;
1581 1 : if (bLastCurveWasApproximateArcInvertedAxisOrder)
1582 1 : dfDistance = OGR_GreatCircle_Distance(
1583 : p.getX(), p.getY(), p2.getX(), p2.getY(), dfSemiMajor);
1584 : else
1585 0 : dfDistance = OGR_GreatCircle_Distance(
1586 : p.getY(), p.getX(), p2.getY(), p2.getX(), dfSemiMajor);
1587 1 : if (dfDistance < dfLastCurveApproximateArcRadius / 5.0)
1588 : {
1589 1 : CPLDebug("OGR", "Moving approximate end of "
1590 : "ArcByCenterPoint to start of "
1591 : "curve");
1592 1 : poLS->setPoint(poLS->getNumPoints() - 1, &p);
1593 : }
1594 : }
1595 :
1596 38 : if (poRing->getNumPoints() < 2 || !poRing->get_IsClosed())
1597 : {
1598 0 : ReportFailure("Non-closed ring");
1599 0 : return nullptr;
1600 : }
1601 38 : return poRing;
1602 : }
1603 :
1604 8 : if (poCC == nullptr)
1605 2 : return nullptr;
1606 :
1607 6 : else if (/* bCastToLinearTypeIfPossible &&*/ bChildrenAreAllLineString)
1608 : {
1609 10 : return std::unique_ptr<OGRLinearRing>(
1610 10 : OGRCurve::CastToLinearRing(poCC.release()));
1611 : }
1612 : else
1613 : {
1614 1 : if (poCC->getNumPoints() < 2 || !poCC->get_IsClosed())
1615 : {
1616 0 : ReportFailure("Non-closed ring");
1617 0 : return nullptr;
1618 : }
1619 1 : return poCC;
1620 : }
1621 : }
1622 :
1623 : /* -------------------------------------------------------------------- */
1624 : /* LineString */
1625 : /* -------------------------------------------------------------------- */
1626 6321 : if (EQUAL(pszBaseGeometry, "LineString") ||
1627 5541 : EQUAL(pszBaseGeometry, "LineStringSegment") ||
1628 4860 : EQUAL(pszBaseGeometry, "Geodesic") ||
1629 4858 : EQUAL(pszBaseGeometry, "GeodesicString"))
1630 : {
1631 2956 : auto poLine = std::make_unique<OGRLineString>();
1632 :
1633 1478 : if (!ParseGMLCoordinates(psNode, poLine.get(), nSRSDimension))
1634 : {
1635 10 : return nullptr;
1636 : }
1637 :
1638 1468 : return poLine;
1639 : }
1640 :
1641 : /* -------------------------------------------------------------------- */
1642 : /* Arc */
1643 : /* -------------------------------------------------------------------- */
1644 4843 : if (EQUAL(pszBaseGeometry, "Arc"))
1645 : {
1646 50 : auto poCC = std::make_unique<OGRCircularString>();
1647 :
1648 25 : if (!ParseGMLCoordinates(psNode, poCC.get(), nSRSDimension))
1649 : {
1650 1 : return nullptr;
1651 : }
1652 :
1653 : // Normally a gml:Arc has only 3 points of controls, but in the
1654 : // wild we sometimes find GML with 5 points, so accept any odd
1655 : // number >= 3 (ArcString should be used for > 3 points)
1656 24 : if (poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1)
1657 : {
1658 2 : ReportFailure("Bad number of points in Arc");
1659 2 : return nullptr;
1660 : }
1661 :
1662 22 : return poCC;
1663 : }
1664 :
1665 : /* -------------------------------------------------------------------- */
1666 : /* ArcString */
1667 : /* -------------------------------------------------------------------- */
1668 4818 : if (EQUAL(pszBaseGeometry, "ArcString"))
1669 : {
1670 56 : auto poCC = std::make_unique<OGRCircularString>();
1671 :
1672 28 : if (!ParseGMLCoordinates(psNode, poCC.get(), nSRSDimension))
1673 : {
1674 1 : return nullptr;
1675 : }
1676 :
1677 27 : if (poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1)
1678 : {
1679 2 : ReportFailure("Bad number of points in ArcString");
1680 2 : return nullptr;
1681 : }
1682 :
1683 25 : return poCC;
1684 : }
1685 :
1686 : /* -------------------------------------------------------------------- */
1687 : /* Circle */
1688 : /* -------------------------------------------------------------------- */
1689 4790 : if (EQUAL(pszBaseGeometry, "Circle"))
1690 : {
1691 58 : auto poLine = std::make_unique<OGRLineString>();
1692 :
1693 29 : if (!ParseGMLCoordinates(psNode, poLine.get(), nSRSDimension))
1694 : {
1695 1 : return nullptr;
1696 : }
1697 :
1698 28 : if (poLine->getNumPoints() != 3)
1699 : {
1700 1 : ReportFailure("Bad number of points in Circle");
1701 1 : return nullptr;
1702 : }
1703 :
1704 27 : double R = 0.0;
1705 27 : double cx = 0.0;
1706 27 : double cy = 0.0;
1707 27 : double alpha0 = 0.0;
1708 27 : double alpha1 = 0.0;
1709 27 : double alpha2 = 0.0;
1710 162 : if (!OGRGeometryFactory::GetCurveParameters(
1711 81 : poLine->getX(0), poLine->getY(0), poLine->getX(1),
1712 81 : poLine->getY(1), poLine->getX(2), poLine->getY(2), R, cx, cy,
1713 : alpha0, alpha1, alpha2))
1714 : {
1715 0 : return nullptr;
1716 : }
1717 :
1718 54 : auto poCC = std::make_unique<OGRCircularString>();
1719 54 : OGRPoint p;
1720 27 : poLine->getPoint(0, &p);
1721 27 : poCC->addPoint(&p);
1722 27 : poLine->getPoint(1, &p);
1723 27 : poCC->addPoint(&p);
1724 27 : poLine->getPoint(2, &p);
1725 27 : poCC->addPoint(&p);
1726 27 : const double alpha4 =
1727 27 : alpha2 > alpha0 ? alpha0 + kdf2PI : alpha0 - kdf2PI;
1728 27 : const double alpha3 = (alpha2 + alpha4) / 2.0;
1729 27 : const double x = cx + R * cos(alpha3);
1730 27 : const double y = cy + R * sin(alpha3);
1731 27 : if (poCC->getCoordinateDimension() == 3)
1732 0 : poCC->addPoint(x, y, p.getZ());
1733 : else
1734 27 : poCC->addPoint(x, y);
1735 27 : poLine->getPoint(0, &p);
1736 27 : poCC->addPoint(&p);
1737 27 : return poCC;
1738 : }
1739 :
1740 : /* -------------------------------------------------------------------- */
1741 : /* ArcByBulge */
1742 : /* -------------------------------------------------------------------- */
1743 4761 : if (EQUAL(pszBaseGeometry, "ArcByBulge"))
1744 : {
1745 6 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "bulge");
1746 6 : if (psChild == nullptr || psChild->eType != CXT_Element ||
1747 4 : psChild->psChild == nullptr)
1748 : {
1749 3 : ReportFailure("Missing bulge element.");
1750 3 : return nullptr;
1751 : }
1752 3 : const double dfBulge = CPLAtof(psChild->psChild->pszValue);
1753 :
1754 3 : psChild = FindBareXMLChild(psNode, "normal");
1755 3 : if (psChild == nullptr || psChild->eType != CXT_Element)
1756 : {
1757 0 : ReportFailure("Missing normal element.");
1758 0 : return nullptr;
1759 : }
1760 3 : double dfNormal = CPLAtof(psChild->psChild->pszValue);
1761 :
1762 6 : auto poLS = std::make_unique<OGRLineString>();
1763 3 : if (!ParseGMLCoordinates(psNode, poLS.get(), nSRSDimension))
1764 : {
1765 1 : return nullptr;
1766 : }
1767 :
1768 2 : if (poLS->getNumPoints() != 2)
1769 : {
1770 1 : ReportFailure("Bad number of points in ArcByBulge");
1771 1 : return nullptr;
1772 : }
1773 :
1774 2 : auto poCC = std::make_unique<OGRCircularString>();
1775 2 : OGRPoint p;
1776 1 : poLS->getPoint(0, &p);
1777 1 : poCC->addPoint(&p);
1778 :
1779 1 : const double dfMidX = (poLS->getX(0) + poLS->getX(1)) / 2.0;
1780 1 : const double dfMidY = (poLS->getY(0) + poLS->getY(1)) / 2.0;
1781 1 : const double dfDirX = (poLS->getX(1) - poLS->getX(0)) / 2.0;
1782 1 : const double dfDirY = (poLS->getY(1) - poLS->getY(0)) / 2.0;
1783 1 : double dfNormX = -dfDirY;
1784 1 : double dfNormY = dfDirX;
1785 1 : const double dfNorm = sqrt(dfNormX * dfNormX + dfNormY * dfNormY);
1786 1 : if (dfNorm != 0.0)
1787 : {
1788 1 : dfNormX /= dfNorm;
1789 1 : dfNormY /= dfNorm;
1790 : }
1791 1 : const double dfNewX = dfMidX + dfNormX * dfBulge * dfNormal;
1792 1 : const double dfNewY = dfMidY + dfNormY * dfBulge * dfNormal;
1793 :
1794 1 : if (poCC->getCoordinateDimension() == 3)
1795 0 : poCC->addPoint(dfNewX, dfNewY, p.getZ());
1796 : else
1797 1 : poCC->addPoint(dfNewX, dfNewY);
1798 :
1799 1 : poLS->getPoint(1, &p);
1800 1 : poCC->addPoint(&p);
1801 :
1802 1 : return poCC;
1803 : }
1804 :
1805 : /* -------------------------------------------------------------------- */
1806 : /* ArcByCenterPoint */
1807 : /* -------------------------------------------------------------------- */
1808 4755 : if (EQUAL(pszBaseGeometry, "ArcByCenterPoint"))
1809 : {
1810 15 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "radius");
1811 15 : if (psChild == nullptr || psChild->eType != CXT_Element)
1812 : {
1813 1 : ReportFailure("Missing radius element.");
1814 1 : return nullptr;
1815 : }
1816 14 : const char *pszUnits = CPLGetXMLValue(psChild, "uom", nullptr);
1817 14 : const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId);
1818 : const double dfRadiusRaw =
1819 14 : CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
1820 14 : double dfRadius = dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw;
1821 :
1822 14 : psChild = FindBareXMLChild(psNode, "startAngle");
1823 14 : if (psChild == nullptr || psChild->eType != CXT_Element)
1824 : {
1825 0 : ReportFailure("Missing startAngle element.");
1826 0 : return nullptr;
1827 : }
1828 : const double dfStartAngle =
1829 14 : CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
1830 :
1831 14 : psChild = FindBareXMLChild(psNode, "endAngle");
1832 14 : if (psChild == nullptr || psChild->eType != CXT_Element)
1833 : {
1834 1 : ReportFailure("Missing endAngle element.");
1835 1 : return nullptr;
1836 : }
1837 : const double dfEndAngle =
1838 13 : CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
1839 :
1840 26 : OGRPoint p;
1841 13 : if (!ParseGMLCoordinates(psNode, &p, nSRSDimension))
1842 : {
1843 1 : return nullptr;
1844 : }
1845 :
1846 12 : bool bSRSUnitIsDegree = false;
1847 12 : bool bInvertedAxisOrder = false;
1848 12 : double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
1849 12 : if (pszSRSName != nullptr)
1850 : {
1851 10 : std::shared_ptr<OGRSpatialReference> poSRS;
1852 10 : if (!oSRSCache.tryGet(pszSRSName, poSRS))
1853 : {
1854 8 : poSRS = std::make_shared<OGRSpatialReference>();
1855 8 : if (poSRS->SetFromUserInput(
1856 : pszSRSName,
1857 : OGRSpatialReference::
1858 8 : SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
1859 : OGRERR_NONE)
1860 0 : poSRS.reset();
1861 8 : oSRSCache.insert(pszSRSName, poSRS);
1862 : }
1863 10 : if (poSRS)
1864 : {
1865 10 : if (poSRS->IsGeographic())
1866 : {
1867 6 : dfSemiMajor = GetSemiMajor(poSRS.get());
1868 : bInvertedAxisOrder =
1869 6 : CPL_TO_BOOL(poSRS->EPSGTreatsAsLatLong());
1870 6 : bSRSUnitIsDegree = fabs(poSRS->GetAngularUnits(nullptr) -
1871 6 : CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
1872 : }
1873 4 : else if (poSRS->IsProjected())
1874 : {
1875 4 : dfSemiMajor = GetSemiMajor(poSRS.get());
1876 : bInvertedAxisOrder =
1877 4 : CPL_TO_BOOL(poSRS->EPSGTreatsAsNorthingEasting());
1878 :
1879 : const double dfSRSUnitsToMetre =
1880 4 : poSRS->GetLinearUnits(nullptr);
1881 4 : if (dfSRSUnitsToMetre > 0)
1882 4 : dfRadius /= dfSRSUnitsToMetre;
1883 : }
1884 : }
1885 : }
1886 :
1887 12 : double dfCenterX = p.getX();
1888 12 : double dfCenterY = p.getY();
1889 :
1890 12 : if (bSRSUnitIsDegree && dfUOMConv > 0)
1891 : {
1892 12 : auto poLS = std::make_unique<OGRLineString>();
1893 6 : const double dfStep = OGRGeometryFactory::GetDefaultArcStepSize();
1894 6 : const double dfSign = dfStartAngle < dfEndAngle ? 1 : -1;
1895 6 : for (double dfAngle = dfStartAngle;
1896 156 : (dfAngle - dfEndAngle) * dfSign < 0;
1897 150 : dfAngle += dfSign * dfStep)
1898 : {
1899 150 : double dfLong = 0.0;
1900 150 : double dfLat = 0.0;
1901 150 : if (bInvertedAxisOrder)
1902 : {
1903 138 : OGR_GreatCircle_ExtendPosition(
1904 : dfCenterX, dfCenterY, dfRadius,
1905 : // See
1906 : // https://ext.eurocontrol.int/aixm_confluence/display/ACG/ArcByCenterPoint+Interpretation+Summary
1907 : dfAngle, dfSemiMajor, &dfLat, &dfLong);
1908 138 : p.setX(dfLat); // yes, external code will do the swap later
1909 138 : p.setY(dfLong);
1910 : }
1911 : else
1912 : {
1913 12 : OGR_GreatCircle_ExtendPosition(
1914 : dfCenterY, dfCenterX, dfRadius, 90 - dfAngle,
1915 : dfSemiMajor, &dfLat, &dfLong);
1916 12 : p.setX(dfLong);
1917 12 : p.setY(dfLat);
1918 : }
1919 150 : poLS->addPoint(&p);
1920 : }
1921 :
1922 6 : double dfLong = 0.0;
1923 6 : double dfLat = 0.0;
1924 6 : if (bInvertedAxisOrder)
1925 : {
1926 5 : OGR_GreatCircle_ExtendPosition(dfCenterX, dfCenterY, dfRadius,
1927 : dfEndAngle, dfSemiMajor, &dfLat,
1928 : &dfLong);
1929 5 : p.setX(dfLat); // yes, external code will do the swap later
1930 5 : p.setY(dfLong);
1931 : }
1932 : else
1933 : {
1934 1 : OGR_GreatCircle_ExtendPosition(dfCenterY, dfCenterX, dfRadius,
1935 : 90 - dfEndAngle, dfSemiMajor,
1936 : &dfLat, &dfLong);
1937 1 : p.setX(dfLong);
1938 1 : p.setY(dfLat);
1939 : }
1940 6 : poLS->addPoint(&p);
1941 :
1942 6 : return poLS;
1943 : }
1944 :
1945 6 : if (bInvertedAxisOrder)
1946 1 : std::swap(dfCenterX, dfCenterY);
1947 :
1948 12 : auto poCC = std::make_unique<OGRCircularString>();
1949 6 : p.setX(dfCenterX + dfRadius * cos(dfStartAngle * kdfD2R));
1950 6 : p.setY(dfCenterY + dfRadius * sin(dfStartAngle * kdfD2R));
1951 6 : poCC->addPoint(&p);
1952 6 : const double dfAverageAngle = (dfStartAngle + dfEndAngle) / 2.0;
1953 6 : p.setX(dfCenterX + dfRadius * cos(dfAverageAngle * kdfD2R));
1954 6 : p.setY(dfCenterY + dfRadius * sin(dfAverageAngle * kdfD2R));
1955 6 : poCC->addPoint(&p);
1956 6 : p.setX(dfCenterX + dfRadius * cos(dfEndAngle * kdfD2R));
1957 6 : p.setY(dfCenterY + dfRadius * sin(dfEndAngle * kdfD2R));
1958 6 : poCC->addPoint(&p);
1959 :
1960 6 : if (bInvertedAxisOrder)
1961 1 : poCC->swapXY();
1962 :
1963 6 : return poCC;
1964 : }
1965 :
1966 : /* -------------------------------------------------------------------- */
1967 : /* CircleByCenterPoint */
1968 : /* -------------------------------------------------------------------- */
1969 4740 : if (EQUAL(pszBaseGeometry, "CircleByCenterPoint"))
1970 : {
1971 7 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "radius");
1972 7 : if (psChild == nullptr || psChild->eType != CXT_Element)
1973 : {
1974 1 : ReportFailure("Missing radius element.");
1975 1 : return nullptr;
1976 : }
1977 6 : const char *pszUnits = CPLGetXMLValue(psChild, "uom", nullptr);
1978 6 : const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId);
1979 : const double dfRadiusRaw =
1980 6 : CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
1981 6 : double dfRadius = dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw;
1982 :
1983 12 : OGRPoint p;
1984 6 : if (!ParseGMLCoordinates(psNode, &p, nSRSDimension))
1985 : {
1986 1 : return nullptr;
1987 : }
1988 :
1989 5 : bool bSRSUnitIsDegree = false;
1990 5 : bool bInvertedAxisOrder = false;
1991 5 : double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
1992 5 : if (pszSRSName != nullptr)
1993 : {
1994 8 : OGRSpatialReference oSRS;
1995 4 : if (oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE)
1996 : {
1997 4 : if (oSRS.IsGeographic())
1998 : {
1999 4 : dfSemiMajor = GetSemiMajor(&oSRS);
2000 : bInvertedAxisOrder =
2001 4 : CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong());
2002 4 : bSRSUnitIsDegree = fabs(oSRS.GetAngularUnits(nullptr) -
2003 4 : CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
2004 : }
2005 0 : else if (oSRS.IsProjected())
2006 : {
2007 0 : dfSemiMajor = GetSemiMajor(&oSRS);
2008 : bInvertedAxisOrder =
2009 0 : CPL_TO_BOOL(oSRS.EPSGTreatsAsNorthingEasting());
2010 :
2011 : const double dfSRSUnitsToMetre =
2012 0 : oSRS.GetLinearUnits(nullptr);
2013 0 : if (dfSRSUnitsToMetre > 0)
2014 0 : dfRadius /= dfSRSUnitsToMetre;
2015 : }
2016 : }
2017 : }
2018 :
2019 5 : double dfCenterX = p.getX();
2020 5 : double dfCenterY = p.getY();
2021 :
2022 5 : if (bSRSUnitIsDegree && dfUOMConv > 0)
2023 : {
2024 4 : auto poLS = std::make_unique<OGRLineString>();
2025 2 : const double dfStep = OGRGeometryFactory::GetDefaultArcStepSize();
2026 182 : for (double dfAngle = 0; dfAngle < 360; dfAngle += dfStep)
2027 : {
2028 180 : double dfLong = 0.0;
2029 180 : double dfLat = 0.0;
2030 180 : if (bInvertedAxisOrder)
2031 : {
2032 90 : OGR_GreatCircle_ExtendPosition(
2033 : dfCenterX, dfCenterY, dfRadius, dfAngle, dfSemiMajor,
2034 : &dfLat, &dfLong);
2035 90 : p.setX(dfLat); // yes, external code will do the swap later
2036 90 : p.setY(dfLong);
2037 : }
2038 : else
2039 : {
2040 90 : OGR_GreatCircle_ExtendPosition(
2041 : dfCenterY, dfCenterX, dfRadius, dfAngle, dfSemiMajor,
2042 : &dfLat, &dfLong);
2043 90 : p.setX(dfLong);
2044 90 : p.setY(dfLat);
2045 : }
2046 180 : poLS->addPoint(&p);
2047 : }
2048 2 : poLS->getPoint(0, &p);
2049 2 : poLS->addPoint(&p);
2050 2 : return poLS;
2051 : }
2052 :
2053 3 : if (bInvertedAxisOrder)
2054 2 : std::swap(dfCenterX, dfCenterY);
2055 :
2056 6 : auto poCC = std::make_unique<OGRCircularString>();
2057 3 : p.setX(dfCenterX - dfRadius);
2058 3 : p.setY(dfCenterY);
2059 3 : poCC->addPoint(&p);
2060 3 : p.setX(dfCenterX);
2061 3 : p.setY(dfCenterY + dfRadius);
2062 3 : poCC->addPoint(&p);
2063 3 : p.setX(dfCenterX + dfRadius);
2064 3 : p.setY(dfCenterY);
2065 3 : poCC->addPoint(&p);
2066 3 : p.setX(dfCenterX);
2067 3 : p.setY(dfCenterY - dfRadius);
2068 3 : poCC->addPoint(&p);
2069 3 : p.setX(dfCenterX - dfRadius);
2070 3 : p.setY(dfCenterY);
2071 3 : poCC->addPoint(&p);
2072 :
2073 3 : if (bInvertedAxisOrder)
2074 2 : poCC->swapXY();
2075 :
2076 3 : return poCC;
2077 : }
2078 :
2079 : /* -------------------------------------------------------------------- */
2080 : /* PointType */
2081 : /* -------------------------------------------------------------------- */
2082 4733 : if (EQUAL(pszBaseGeometry, "PointType") ||
2083 4733 : EQUAL(pszBaseGeometry, "Point") ||
2084 3669 : EQUAL(pszBaseGeometry, "ElevatedPoint") ||
2085 3668 : EQUAL(pszBaseGeometry, "ConnectionPoint"))
2086 : {
2087 2130 : auto poPoint = std::make_unique<OGRPoint>();
2088 :
2089 1065 : if (!ParseGMLCoordinates(psNode, poPoint.get(), nSRSDimension))
2090 : {
2091 20 : return nullptr;
2092 : }
2093 :
2094 1045 : return poPoint;
2095 : }
2096 :
2097 : /* -------------------------------------------------------------------- */
2098 : /* Box */
2099 : /* -------------------------------------------------------------------- */
2100 3668 : if (EQUAL(pszBaseGeometry, "BoxType") || EQUAL(pszBaseGeometry, "Box"))
2101 : {
2102 12 : OGRLineString oPoints;
2103 :
2104 6 : if (!ParseGMLCoordinates(psNode, &oPoints, nSRSDimension))
2105 1 : return nullptr;
2106 :
2107 5 : if (oPoints.getNumPoints() < 2)
2108 1 : return nullptr;
2109 :
2110 8 : auto poBoxRing = std::make_unique<OGRLinearRing>();
2111 8 : auto poBoxPoly = std::make_unique<OGRPolygon>();
2112 :
2113 4 : poBoxRing->setNumPoints(5);
2114 4 : poBoxRing->setPoint(0, oPoints.getX(0), oPoints.getY(0),
2115 : oPoints.getZ(0));
2116 4 : poBoxRing->setPoint(1, oPoints.getX(1), oPoints.getY(0),
2117 : oPoints.getZ(0));
2118 4 : poBoxRing->setPoint(2, oPoints.getX(1), oPoints.getY(1),
2119 : oPoints.getZ(1));
2120 4 : poBoxRing->setPoint(3, oPoints.getX(0), oPoints.getY(1),
2121 : oPoints.getZ(0));
2122 4 : poBoxRing->setPoint(4, oPoints.getX(0), oPoints.getY(0),
2123 : oPoints.getZ(0));
2124 4 : poBoxRing->set3D(oPoints.Is3D());
2125 :
2126 4 : poBoxPoly->addRing(std::move(poBoxRing));
2127 :
2128 4 : return poBoxPoly;
2129 : }
2130 :
2131 : /* -------------------------------------------------------------------- */
2132 : /* Envelope */
2133 : /* -------------------------------------------------------------------- */
2134 3662 : if (EQUAL(pszBaseGeometry, "Envelope"))
2135 : {
2136 : const CPLXMLNode *psLowerCorner =
2137 26 : FindBareXMLChild(psNode, "lowerCorner");
2138 : const CPLXMLNode *psUpperCorner =
2139 26 : FindBareXMLChild(psNode, "upperCorner");
2140 26 : if (psLowerCorner == nullptr || psUpperCorner == nullptr)
2141 2 : return nullptr;
2142 24 : const char *pszLowerCorner = GetElementText(psLowerCorner);
2143 24 : const char *pszUpperCorner = GetElementText(psUpperCorner);
2144 24 : if (pszLowerCorner == nullptr || pszUpperCorner == nullptr)
2145 1 : return nullptr;
2146 23 : char **papszLowerCorner = CSLTokenizeString(pszLowerCorner);
2147 23 : char **papszUpperCorner = CSLTokenizeString(pszUpperCorner);
2148 23 : const int nTokenCountLC = CSLCount(papszLowerCorner);
2149 23 : const int nTokenCountUC = CSLCount(papszUpperCorner);
2150 23 : if (nTokenCountLC < 2 || nTokenCountUC < 2)
2151 : {
2152 0 : CSLDestroy(papszLowerCorner);
2153 0 : CSLDestroy(papszUpperCorner);
2154 0 : return nullptr;
2155 : }
2156 :
2157 23 : const double dfLLX = CPLAtof(papszLowerCorner[0]);
2158 23 : const double dfLLY = CPLAtof(papszLowerCorner[1]);
2159 23 : const double dfURX = CPLAtof(papszUpperCorner[0]);
2160 23 : const double dfURY = CPLAtof(papszUpperCorner[1]);
2161 23 : CSLDestroy(papszLowerCorner);
2162 23 : CSLDestroy(papszUpperCorner);
2163 :
2164 46 : auto poEnvelopeRing = std::make_unique<OGRLinearRing>();
2165 46 : auto poPoly = std::make_unique<OGRPolygon>();
2166 :
2167 23 : poEnvelopeRing->setNumPoints(5);
2168 23 : poEnvelopeRing->setPoint(0, dfLLX, dfLLY);
2169 23 : poEnvelopeRing->setPoint(1, dfURX, dfLLY);
2170 23 : poEnvelopeRing->setPoint(2, dfURX, dfURY);
2171 23 : poEnvelopeRing->setPoint(3, dfLLX, dfURY);
2172 23 : poEnvelopeRing->setPoint(4, dfLLX, dfLLY);
2173 23 : poPoly->addRing(std::move(poEnvelopeRing));
2174 :
2175 23 : return poPoly;
2176 : }
2177 :
2178 : /* --------------------------------------------------------------------- */
2179 : /* MultiPolygon / MultiSurface / CompositeSurface */
2180 : /* */
2181 : /* For CompositeSurface, this is a very rough approximation to deal with */
2182 : /* it as a MultiPolygon, because it can several faces of a 3D volume. */
2183 : /* --------------------------------------------------------------------- */
2184 3636 : if (EQUAL(pszBaseGeometry, "MultiPolygon") ||
2185 3614 : EQUAL(pszBaseGeometry, "MultiSurface") ||
2186 3546 : EQUAL(pszBaseGeometry, "Shell") || // CityGML 3 uses this
2187 3546 : EQUAL(pszBaseGeometry, "CompositeSurface"))
2188 : {
2189 : std::unique_ptr<OGRMultiSurface> poMS =
2190 91 : EQUAL(pszBaseGeometry, "MultiPolygon")
2191 113 : ? std::make_unique<OGRMultiPolygon>()
2192 204 : : std::make_unique<OGRMultiSurface>();
2193 91 : bool bReconstructTopology = false;
2194 91 : bool bChildrenAreAllPolygons = true;
2195 :
2196 : // Iterate over children.
2197 253 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2198 162 : psChild = psChild->psNext)
2199 : {
2200 164 : const char *pszMemberElement = BareGMLElement(psChild->pszValue);
2201 164 : if (psChild->eType == CXT_Element &&
2202 97 : (EQUAL(pszMemberElement, "polygonMember") ||
2203 76 : EQUAL(pszMemberElement, "surfaceMember")))
2204 : {
2205 91 : const CPLXMLNode *psSurfaceChild = GetChildElement(psChild);
2206 :
2207 91 : if (psSurfaceChild != nullptr)
2208 : {
2209 : // Cf #5421 where there are PolygonPatch with only inner
2210 : // rings.
2211 : const CPLXMLNode *psPolygonPatch =
2212 90 : GetChildElement(GetChildElement(psSurfaceChild));
2213 90 : const CPLXMLNode *psPolygonPatchChild = nullptr;
2214 178 : if (psPolygonPatch != nullptr &&
2215 88 : psPolygonPatch->eType == CXT_Element &&
2216 88 : EQUAL(BareGMLElement(psPolygonPatch->pszValue),
2217 13 : "PolygonPatch") &&
2218 : (psPolygonPatchChild =
2219 191 : GetChildElement(psPolygonPatch)) != nullptr &&
2220 13 : EQUAL(BareGMLElement(psPolygonPatchChild->pszValue),
2221 : "interior"))
2222 : {
2223 : // Find all inner rings
2224 1 : for (const CPLXMLNode *psChild2 =
2225 : psPolygonPatch->psChild;
2226 2 : psChild2 != nullptr; psChild2 = psChild2->psNext)
2227 : {
2228 2 : if (psChild2->eType == CXT_Element &&
2229 1 : (EQUAL(BareGMLElement(psChild2->pszValue),
2230 : "interior")))
2231 : {
2232 : const CPLXMLNode *psInteriorChild =
2233 1 : GetChildElement(psChild2);
2234 : auto poRing =
2235 : psInteriorChild == nullptr
2236 : ? nullptr
2237 : : GML2OGRGeometry_XMLNode_Internal(
2238 : psInteriorChild, pszId,
2239 : nPseudoBoolGetSecondaryGeometryOption,
2240 : nRecLevel + 1, nSRSDimension,
2241 1 : pszSRSName, oSRSCache);
2242 1 : if (poRing == nullptr)
2243 : {
2244 0 : ReportFailure("Invalid interior ring");
2245 0 : return nullptr;
2246 : }
2247 1 : if (!EQUAL(poRing->getGeometryName(),
2248 : "LINEARRING"))
2249 : {
2250 0 : ReportFailure("%s: Got %s geometry as "
2251 : "innerBoundaryIs instead of "
2252 : "LINEARRING.",
2253 : pszBaseGeometry,
2254 : poRing->getGeometryName());
2255 0 : return nullptr;
2256 : }
2257 :
2258 1 : bReconstructTopology = true;
2259 2 : auto poPolygon = std::make_unique<OGRPolygon>();
2260 : auto poLinearRing =
2261 : std::unique_ptr<OGRLinearRing>(
2262 1 : poRing.release()->toLinearRing());
2263 1 : poPolygon->addRing(std::move(poLinearRing));
2264 1 : poMS->addGeometry(std::move(poPolygon));
2265 : }
2266 : }
2267 : }
2268 : else
2269 : {
2270 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2271 : psSurfaceChild, pszId,
2272 : nPseudoBoolGetSecondaryGeometryOption,
2273 : nRecLevel + 1, nSRSDimension, pszSRSName,
2274 89 : oSRSCache);
2275 89 : if (!GML2OGRGeometry_AddToMultiSurface(
2276 89 : poMS.get(), std::move(poGeom), pszMemberElement,
2277 : bChildrenAreAllPolygons))
2278 : {
2279 2 : return nullptr;
2280 : }
2281 : }
2282 89 : }
2283 : }
2284 73 : else if (psChild->eType == CXT_Element &&
2285 6 : EQUAL(pszMemberElement, "surfaceMembers"))
2286 : {
2287 5 : for (const CPLXMLNode *psChild2 = psChild->psChild;
2288 11 : psChild2 != nullptr; psChild2 = psChild2->psNext)
2289 : {
2290 6 : pszMemberElement = BareGMLElement(psChild2->pszValue);
2291 6 : if (psChild2->eType == CXT_Element &&
2292 5 : (EQUAL(pszMemberElement, "Surface") ||
2293 3 : EQUAL(pszMemberElement, "Polygon") ||
2294 2 : EQUAL(pszMemberElement, "PolygonPatch") ||
2295 2 : EQUAL(pszMemberElement,
2296 2 : "Shell") || // CityGML 3 uses this
2297 2 : EQUAL(pszMemberElement, "CompositeSurface")))
2298 : {
2299 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2300 : psChild2, pszId,
2301 : nPseudoBoolGetSecondaryGeometryOption,
2302 : nRecLevel + 1, nSRSDimension, pszSRSName,
2303 4 : oSRSCache);
2304 4 : if (!GML2OGRGeometry_AddToMultiSurface(
2305 4 : poMS.get(), std::move(poGeom), pszMemberElement,
2306 : bChildrenAreAllPolygons))
2307 : {
2308 0 : return nullptr;
2309 : }
2310 : }
2311 : }
2312 : }
2313 : }
2314 :
2315 89 : if (bReconstructTopology && bChildrenAreAllPolygons)
2316 : {
2317 : auto poMPoly =
2318 1 : wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface
2319 : ? std::unique_ptr<OGRMultiPolygon>(
2320 : OGRMultiSurface::CastToMultiPolygon(poMS.release()))
2321 : : std::unique_ptr<OGRMultiPolygon>(
2322 2 : poMS.release()->toMultiPolygon());
2323 1 : const int nPolygonCount = poMPoly->getNumGeometries();
2324 2 : std::vector<OGRGeometry *> apoPolygons;
2325 1 : apoPolygons.reserve(nPolygonCount);
2326 4 : for (int i = 0; i < nPolygonCount; i++)
2327 : {
2328 3 : apoPolygons.emplace_back(poMPoly->getGeometryRef(0));
2329 3 : poMPoly->removeGeometry(0, FALSE);
2330 : }
2331 1 : poMPoly.reset();
2332 1 : int bResultValidGeometry = FALSE;
2333 : return std::unique_ptr<OGRGeometry>(
2334 : OGRGeometryFactory::organizePolygons(
2335 1 : apoPolygons.data(), nPolygonCount, &bResultValidGeometry));
2336 : }
2337 : else
2338 : {
2339 88 : if (/* bCastToLinearTypeIfPossible && */
2340 88 : wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface &&
2341 : bChildrenAreAllPolygons)
2342 : {
2343 114 : return std::unique_ptr<OGRMultiPolygon>(
2344 57 : OGRMultiSurface::CastToMultiPolygon(poMS.release()));
2345 : }
2346 :
2347 31 : return poMS;
2348 : }
2349 : }
2350 :
2351 : /* -------------------------------------------------------------------- */
2352 : /* MultiPoint */
2353 : /* -------------------------------------------------------------------- */
2354 3545 : if (EQUAL(pszBaseGeometry, "MultiPoint"))
2355 : {
2356 64 : auto poMP = std::make_unique<OGRMultiPoint>();
2357 :
2358 : // Collect points.
2359 112 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2360 80 : psChild = psChild->psNext)
2361 : {
2362 131 : if (psChild->eType == CXT_Element &&
2363 49 : EQUAL(BareGMLElement(psChild->pszValue), "pointMember"))
2364 : {
2365 41 : const CPLXMLNode *psPointChild = GetChildElement(psChild);
2366 :
2367 41 : if (psPointChild != nullptr)
2368 : {
2369 : auto poPointMember = GML2OGRGeometry_XMLNode_Internal(
2370 : psPointChild, pszId,
2371 : nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
2372 40 : nSRSDimension, pszSRSName, oSRSCache);
2373 80 : if (poPointMember == nullptr ||
2374 40 : wkbFlatten(poPointMember->getGeometryType()) !=
2375 : wkbPoint)
2376 : {
2377 1 : ReportFailure("MultiPoint: Got %s geometry as "
2378 : "pointMember instead of POINT",
2379 : poPointMember
2380 : ? poPointMember->getGeometryName()
2381 : : "NULL");
2382 1 : return nullptr;
2383 : }
2384 :
2385 39 : poMP->addGeometry(std::move(poPointMember));
2386 : }
2387 : }
2388 49 : else if (psChild->eType == CXT_Element &&
2389 8 : EQUAL(BareGMLElement(psChild->pszValue), "pointMembers"))
2390 : {
2391 7 : for (const CPLXMLNode *psChild2 = psChild->psChild;
2392 14 : psChild2 != nullptr; psChild2 = psChild2->psNext)
2393 : {
2394 15 : if (psChild2->eType == CXT_Element &&
2395 7 : (EQUAL(BareGMLElement(psChild2->pszValue), "Point")))
2396 : {
2397 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2398 : psChild2, pszId,
2399 : nPseudoBoolGetSecondaryGeometryOption,
2400 : nRecLevel + 1, nSRSDimension, pszSRSName,
2401 6 : oSRSCache);
2402 6 : if (poGeom == nullptr)
2403 : {
2404 1 : ReportFailure("Invalid %s",
2405 : BareGMLElement(psChild2->pszValue));
2406 1 : return nullptr;
2407 : }
2408 :
2409 5 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
2410 : {
2411 : auto poPoint = std::unique_ptr<OGRPoint>(
2412 5 : poGeom.release()->toPoint());
2413 5 : poMP->addGeometry(std::move(poPoint));
2414 : }
2415 : else
2416 : {
2417 0 : ReportFailure("Got %s geometry as pointMember "
2418 : "instead of POINT.",
2419 : poGeom->getGeometryName());
2420 0 : return nullptr;
2421 : }
2422 : }
2423 : }
2424 : }
2425 : }
2426 :
2427 30 : return poMP;
2428 : }
2429 :
2430 : /* -------------------------------------------------------------------- */
2431 : /* MultiLineString */
2432 : /* -------------------------------------------------------------------- */
2433 3513 : if (EQUAL(pszBaseGeometry, "MultiLineString"))
2434 : {
2435 36 : auto poMLS = std::make_unique<OGRMultiLineString>();
2436 :
2437 : // Collect lines.
2438 49 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2439 31 : psChild = psChild->psNext)
2440 : {
2441 53 : if (psChild->eType == CXT_Element &&
2442 20 : EQUAL(BareGMLElement(psChild->pszValue), "lineStringMember"))
2443 : {
2444 19 : const CPLXMLNode *psLineStringChild = GetChildElement(psChild);
2445 : auto poGeom = psLineStringChild == nullptr
2446 : ? nullptr
2447 : : GML2OGRGeometry_XMLNode_Internal(
2448 : psLineStringChild, pszId,
2449 : nPseudoBoolGetSecondaryGeometryOption,
2450 : nRecLevel + 1, nSRSDimension,
2451 19 : pszSRSName, oSRSCache);
2452 37 : if (poGeom == nullptr ||
2453 18 : wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2454 : {
2455 2 : ReportFailure("MultiLineString: Got %s geometry as Member "
2456 : "instead of LINESTRING.",
2457 : poGeom ? poGeom->getGeometryName() : "NULL");
2458 2 : return nullptr;
2459 : }
2460 :
2461 17 : poMLS->addGeometry(std::move(poGeom));
2462 : }
2463 : }
2464 :
2465 16 : return poMLS;
2466 : }
2467 :
2468 : /* -------------------------------------------------------------------- */
2469 : /* MultiCurve */
2470 : /* -------------------------------------------------------------------- */
2471 3495 : if (EQUAL(pszBaseGeometry, "MultiCurve"))
2472 : {
2473 116 : auto poMC = std::make_unique<OGRMultiCurve>();
2474 58 : bool bChildrenAreAllLineString = true;
2475 :
2476 : // Collect curveMembers.
2477 141 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2478 83 : psChild = psChild->psNext)
2479 : {
2480 151 : if (psChild->eType == CXT_Element &&
2481 62 : EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
2482 : {
2483 55 : const CPLXMLNode *psChild2 = GetChildElement(psChild);
2484 55 : if (psChild2 != nullptr) // Empty curveMember is valid.
2485 : {
2486 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2487 : psChild2, pszId, nPseudoBoolGetSecondaryGeometryOption,
2488 54 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
2489 104 : if (poGeom == nullptr ||
2490 50 : !OGR_GT_IsCurve(poGeom->getGeometryType()))
2491 : {
2492 4 : ReportFailure("MultiCurve: Got %s geometry as Member "
2493 : "instead of a curve.",
2494 : poGeom ? poGeom->getGeometryName()
2495 : : "NULL");
2496 4 : return nullptr;
2497 : }
2498 :
2499 50 : if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2500 12 : bChildrenAreAllLineString = false;
2501 :
2502 50 : if (poMC->addGeometry(std::move(poGeom)) != OGRERR_NONE)
2503 : {
2504 0 : return nullptr;
2505 : }
2506 : }
2507 : }
2508 41 : else if (psChild->eType == CXT_Element &&
2509 7 : EQUAL(BareGMLElement(psChild->pszValue), "curveMembers"))
2510 : {
2511 6 : for (const CPLXMLNode *psChild2 = psChild->psChild;
2512 10 : psChild2 != nullptr; psChild2 = psChild2->psNext)
2513 : {
2514 6 : if (psChild2->eType == CXT_Element)
2515 : {
2516 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2517 : psChild2, pszId,
2518 : nPseudoBoolGetSecondaryGeometryOption,
2519 : nRecLevel + 1, nSRSDimension, pszSRSName,
2520 5 : oSRSCache);
2521 8 : if (poGeom == nullptr ||
2522 3 : !OGR_GT_IsCurve(poGeom->getGeometryType()))
2523 : {
2524 2 : ReportFailure("MultiCurve: Got %s geometry as "
2525 : "Member instead of a curve.",
2526 : poGeom ? poGeom->getGeometryName()
2527 : : "NULL");
2528 2 : return nullptr;
2529 : }
2530 :
2531 3 : if (wkbFlatten(poGeom->getGeometryType()) !=
2532 : wkbLineString)
2533 0 : bChildrenAreAllLineString = false;
2534 :
2535 3 : if (poMC->addGeometry(std::move(poGeom)) != OGRERR_NONE)
2536 : {
2537 0 : return nullptr;
2538 : }
2539 : }
2540 : }
2541 : }
2542 : }
2543 :
2544 52 : if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString)
2545 : {
2546 80 : return std::unique_ptr<OGRMultiLineString>(
2547 40 : OGRMultiCurve::CastToMultiLineString(poMC.release()));
2548 : }
2549 :
2550 12 : return poMC;
2551 : }
2552 :
2553 : /* -------------------------------------------------------------------- */
2554 : /* CompositeCurve */
2555 : /* -------------------------------------------------------------------- */
2556 3437 : if (EQUAL(pszBaseGeometry, "CompositeCurve"))
2557 : {
2558 36 : auto poCC = std::make_unique<OGRCompoundCurve>();
2559 18 : bool bChildrenAreAllLineString = true;
2560 :
2561 : // Collect curveMembers.
2562 41 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2563 23 : psChild = psChild->psNext)
2564 : {
2565 47 : if (psChild->eType == CXT_Element &&
2566 23 : EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
2567 : {
2568 22 : const CPLXMLNode *psChild2 = GetChildElement(psChild);
2569 22 : if (psChild2 != nullptr) // Empty curveMember is valid.
2570 : {
2571 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2572 : psChild2, pszId, nPseudoBoolGetSecondaryGeometryOption,
2573 22 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
2574 22 : if (!GML2OGRGeometry_AddToCompositeCurve(
2575 22 : poCC.get(), std::move(poGeom),
2576 : bChildrenAreAllLineString))
2577 : {
2578 1 : return nullptr;
2579 : }
2580 : }
2581 : }
2582 3 : else if (psChild->eType == CXT_Element &&
2583 1 : EQUAL(BareGMLElement(psChild->pszValue), "curveMembers"))
2584 : {
2585 1 : for (const CPLXMLNode *psChild2 = psChild->psChild;
2586 4 : psChild2 != nullptr; psChild2 = psChild2->psNext)
2587 : {
2588 3 : if (psChild2->eType == CXT_Element)
2589 : {
2590 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2591 : psChild2, pszId,
2592 : nPseudoBoolGetSecondaryGeometryOption,
2593 : nRecLevel + 1, nSRSDimension, pszSRSName,
2594 2 : oSRSCache);
2595 2 : if (!GML2OGRGeometry_AddToCompositeCurve(
2596 2 : poCC.get(), std::move(poGeom),
2597 : bChildrenAreAllLineString))
2598 : {
2599 0 : return nullptr;
2600 : }
2601 : }
2602 : }
2603 : }
2604 : }
2605 :
2606 17 : if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString)
2607 : {
2608 10 : return std::unique_ptr<OGRLineString>(
2609 10 : OGRCurve::CastToLineString(poCC.release()));
2610 : }
2611 :
2612 12 : return poCC;
2613 : }
2614 :
2615 : /* -------------------------------------------------------------------- */
2616 : /* Curve */
2617 : /* -------------------------------------------------------------------- */
2618 3419 : if (EQUAL(pszBaseGeometry, "Curve") ||
2619 2667 : EQUAL(pszBaseGeometry, "ElevatedCurve") /* AIXM */)
2620 : {
2621 758 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "segments");
2622 758 : if (psChild == nullptr)
2623 : {
2624 5 : ReportFailure("GML3 Curve geometry lacks segments element.");
2625 5 : return nullptr;
2626 : }
2627 :
2628 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2629 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
2630 1506 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
2631 753 : if (poGeom == nullptr || !OGR_GT_IsCurve(poGeom->getGeometryType()))
2632 : {
2633 2 : ReportFailure(
2634 : "Curve: Got %s geometry as Member instead of segments.",
2635 : poGeom ? poGeom->getGeometryName() : "NULL");
2636 2 : return nullptr;
2637 : }
2638 :
2639 751 : return poGeom;
2640 : }
2641 :
2642 : /* -------------------------------------------------------------------- */
2643 : /* segments */
2644 : /* -------------------------------------------------------------------- */
2645 2661 : if (EQUAL(pszBaseGeometry, "segments"))
2646 : {
2647 757 : std::unique_ptr<OGRCurve> poCurve;
2648 757 : std::unique_ptr<OGRCompoundCurve> poCC;
2649 757 : bool bChildrenAreAllLineString = true;
2650 :
2651 757 : bool bLastCurveWasApproximateArc = false;
2652 757 : bool bLastCurveWasApproximateArcInvertedAxisOrder = false;
2653 757 : double dfLastCurveApproximateArcRadius = 0.0;
2654 757 : double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
2655 :
2656 1531 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2657 774 : psChild = psChild->psNext)
2658 :
2659 : {
2660 780 : if (psChild->eType == CXT_Element
2661 : // && (EQUAL(BareGMLElement(psChild->pszValue),
2662 : // "LineStringSegment") ||
2663 : // EQUAL(BareGMLElement(psChild->pszValue),
2664 : // "GeodesicString") ||
2665 : // EQUAL(BareGMLElement(psChild->pszValue), "Arc") ||
2666 : // EQUAL(BareGMLElement(psChild->pszValue), "Circle"))
2667 : )
2668 : {
2669 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2670 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
2671 779 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
2672 1553 : if (poGeom == nullptr ||
2673 774 : !OGR_GT_IsCurve(poGeom->getGeometryType()))
2674 : {
2675 6 : ReportFailure("segments: Got %s geometry as Member "
2676 : "instead of curve.",
2677 : poGeom ? poGeom->getGeometryName() : "NULL");
2678 6 : return nullptr;
2679 : }
2680 :
2681 : // Ad-hoc logic to handle nicely connecting ArcByCenterPoint
2682 : // with consecutive curves, as found in some AIXM files.
2683 773 : bool bIsApproximateArc = false;
2684 773 : if (strcmp(BareGMLElement(psChild->pszValue),
2685 773 : "ArcByCenterPoint") == 0)
2686 : {
2687 7 : storeArcByCenterPointParameters(
2688 : psChild, pszSRSName, bIsApproximateArc,
2689 : dfLastCurveApproximateArcRadius,
2690 : bLastCurveWasApproximateArcInvertedAxisOrder,
2691 : dfSemiMajor);
2692 : }
2693 :
2694 773 : if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2695 70 : bChildrenAreAllLineString = false;
2696 :
2697 773 : if (poCC == nullptr && poCurve == nullptr)
2698 : {
2699 750 : poCurve.reset(poGeom.release()->toCurve());
2700 : }
2701 : else
2702 : {
2703 23 : if (poCC == nullptr)
2704 : {
2705 13 : poCC = std::make_unique<OGRCompoundCurve>();
2706 13 : if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE)
2707 : {
2708 0 : return nullptr;
2709 : }
2710 13 : poCurve.reset();
2711 : }
2712 :
2713 23 : connectArcByCenterPointToOtherSegments(
2714 : poGeom.get(), poCC.get(), bIsApproximateArc,
2715 : bLastCurveWasApproximateArc,
2716 : dfLastCurveApproximateArcRadius,
2717 : bLastCurveWasApproximateArcInvertedAxisOrder,
2718 : dfSemiMajor);
2719 :
2720 : auto poAsCurve =
2721 23 : std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
2722 23 : if (poCC->addCurve(std::move(poAsCurve)) != OGRERR_NONE)
2723 : {
2724 0 : return nullptr;
2725 : }
2726 : }
2727 :
2728 773 : bLastCurveWasApproximateArc = bIsApproximateArc;
2729 : }
2730 : }
2731 :
2732 751 : if (poCurve != nullptr)
2733 737 : return poCurve;
2734 14 : if (poCC == nullptr)
2735 1 : return std::make_unique<OGRLineString>();
2736 :
2737 13 : if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString)
2738 : {
2739 6 : return std::unique_ptr<OGRLineString>(
2740 6 : OGRCurve::CastToLineString(poCC.release()));
2741 : }
2742 :
2743 10 : return poCC;
2744 : }
2745 :
2746 : /* -------------------------------------------------------------------- */
2747 : /* MultiGeometry */
2748 : /* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is */
2749 : /* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */
2750 : /* tolerant with the non compliant files we produced. */
2751 : /* -------------------------------------------------------------------- */
2752 1904 : if (EQUAL(pszBaseGeometry, "MultiGeometry") ||
2753 1819 : EQUAL(pszBaseGeometry, "GeometryCollection"))
2754 : {
2755 170 : auto poGC = std::make_unique<OGRGeometryCollection>();
2756 :
2757 : // Collect geoms.
2758 156 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
2759 71 : psChild = psChild->psNext)
2760 : {
2761 196 : if (psChild->eType == CXT_Element &&
2762 91 : EQUAL(BareGMLElement(psChild->pszValue), "geometryMember"))
2763 : {
2764 88 : const CPLXMLNode *psGeometryChild = GetChildElement(psChild);
2765 :
2766 88 : if (psGeometryChild != nullptr)
2767 : {
2768 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2769 : psGeometryChild, pszId,
2770 : nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
2771 87 : nSRSDimension, pszSRSName, oSRSCache);
2772 87 : if (poGeom == nullptr)
2773 : {
2774 33 : ReportFailure(
2775 : "GeometryCollection: Failed to get geometry "
2776 : "in geometryMember");
2777 33 : return nullptr;
2778 : }
2779 :
2780 54 : poGC->addGeometry(std::move(poGeom));
2781 : }
2782 : }
2783 20 : else if (psChild->eType == CXT_Element &&
2784 3 : EQUAL(BareGMLElement(psChild->pszValue),
2785 : "geometryMembers"))
2786 : {
2787 2 : for (const CPLXMLNode *psChild2 = psChild->psChild;
2788 4 : psChild2 != nullptr; psChild2 = psChild2->psNext)
2789 : {
2790 3 : if (psChild2->eType == CXT_Element)
2791 : {
2792 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
2793 : psChild2, pszId,
2794 : nPseudoBoolGetSecondaryGeometryOption,
2795 : nRecLevel + 1, nSRSDimension, pszSRSName,
2796 2 : oSRSCache);
2797 2 : if (poGeom == nullptr)
2798 : {
2799 1 : ReportFailure(
2800 : "GeometryCollection: Failed to get geometry "
2801 : "in geometryMember");
2802 1 : return nullptr;
2803 : }
2804 :
2805 1 : poGC->addGeometry(std::move(poGeom));
2806 : }
2807 : }
2808 : }
2809 : }
2810 :
2811 51 : return poGC;
2812 : }
2813 :
2814 : /* -------------------------------------------------------------------- */
2815 : /* Directed Edge */
2816 : /* -------------------------------------------------------------------- */
2817 1819 : if (EQUAL(pszBaseGeometry, "directedEdge"))
2818 : {
2819 : // Collect edge.
2820 1226 : const CPLXMLNode *psEdge = FindBareXMLChild(psNode, "Edge");
2821 1226 : if (psEdge == nullptr)
2822 : {
2823 0 : ReportFailure("Failed to get Edge element in directedEdge");
2824 0 : return nullptr;
2825 : }
2826 :
2827 : // TODO(schwehr): Localize vars after removing gotos.
2828 1226 : std::unique_ptr<OGRGeometry> poGeom;
2829 1226 : const CPLXMLNode *psNodeElement = nullptr;
2830 1226 : const CPLXMLNode *psPointProperty = nullptr;
2831 1226 : const CPLXMLNode *psPoint = nullptr;
2832 1226 : bool bNodeOrientation = true;
2833 1226 : std::unique_ptr<OGRPoint> poPositiveNode;
2834 1226 : std::unique_ptr<OGRPoint> poNegativeNode;
2835 :
2836 1226 : const bool bEdgeOrientation = GetElementOrientation(psNode);
2837 :
2838 1226 : if (bGetSecondaryGeometry)
2839 : {
2840 : const CPLXMLNode *psdirectedNode =
2841 0 : FindBareXMLChild(psEdge, "directedNode");
2842 0 : if (psdirectedNode == nullptr)
2843 0 : goto nonode;
2844 :
2845 0 : bNodeOrientation = GetElementOrientation(psdirectedNode);
2846 :
2847 0 : psNodeElement = FindBareXMLChild(psdirectedNode, "Node");
2848 0 : if (psNodeElement == nullptr)
2849 0 : goto nonode;
2850 :
2851 0 : psPointProperty = FindBareXMLChild(psNodeElement, "pointProperty");
2852 0 : if (psPointProperty == nullptr)
2853 : psPointProperty =
2854 0 : FindBareXMLChild(psNodeElement, "connectionPointProperty");
2855 0 : if (psPointProperty == nullptr)
2856 0 : goto nonode;
2857 :
2858 0 : psPoint = FindBareXMLChild(psPointProperty, "Point");
2859 0 : if (psPoint == nullptr)
2860 0 : psPoint = FindBareXMLChild(psPointProperty, "ConnectionPoint");
2861 0 : if (psPoint == nullptr)
2862 0 : goto nonode;
2863 :
2864 0 : poGeom = GML2OGRGeometry_XMLNode_Internal(
2865 : psPoint, pszId, nPseudoBoolGetSecondaryGeometryOption,
2866 0 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache, true);
2867 0 : if (poGeom == nullptr ||
2868 0 : wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
2869 : {
2870 : // ReportFailure(
2871 : // "Got %s geometry as Member instead of POINT.",
2872 : // poGeom ? poGeom->getGeometryName() : "NULL" );
2873 0 : goto nonode;
2874 : }
2875 :
2876 : {
2877 0 : OGRPoint *poPoint = poGeom.release()->toPoint();
2878 0 : if ((bNodeOrientation == bEdgeOrientation) != bOrientation)
2879 0 : poPositiveNode.reset(poPoint);
2880 : else
2881 0 : poNegativeNode.reset(poPoint);
2882 : }
2883 :
2884 : // Look for the other node.
2885 0 : psdirectedNode = psdirectedNode->psNext;
2886 0 : while (psdirectedNode != nullptr &&
2887 0 : !EQUAL(psdirectedNode->pszValue, "directedNode"))
2888 0 : psdirectedNode = psdirectedNode->psNext;
2889 0 : if (psdirectedNode == nullptr)
2890 0 : goto nonode;
2891 :
2892 0 : if (GetElementOrientation(psdirectedNode) == bNodeOrientation)
2893 0 : goto nonode;
2894 :
2895 0 : psNodeElement = FindBareXMLChild(psEdge, "Node");
2896 0 : if (psNodeElement == nullptr)
2897 0 : goto nonode;
2898 :
2899 0 : psPointProperty = FindBareXMLChild(psNodeElement, "pointProperty");
2900 0 : if (psPointProperty == nullptr)
2901 : psPointProperty =
2902 0 : FindBareXMLChild(psNodeElement, "connectionPointProperty");
2903 0 : if (psPointProperty == nullptr)
2904 0 : goto nonode;
2905 :
2906 0 : psPoint = FindBareXMLChild(psPointProperty, "Point");
2907 0 : if (psPoint == nullptr)
2908 0 : psPoint = FindBareXMLChild(psPointProperty, "ConnectionPoint");
2909 0 : if (psPoint == nullptr)
2910 0 : goto nonode;
2911 :
2912 0 : poGeom = GML2OGRGeometry_XMLNode_Internal(
2913 : psPoint, pszId, nPseudoBoolGetSecondaryGeometryOption,
2914 0 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache, true);
2915 0 : if (poGeom == nullptr ||
2916 0 : wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
2917 : {
2918 : // ReportFailure(
2919 : // "Got %s geometry as Member instead of POINT.",
2920 : // poGeom ? poGeom->getGeometryName() : "NULL" );
2921 0 : goto nonode;
2922 : }
2923 :
2924 : {
2925 0 : OGRPoint *poPoint = poGeom.release()->toPoint();
2926 0 : if ((bNodeOrientation == bEdgeOrientation) != bOrientation)
2927 0 : poNegativeNode.reset(poPoint);
2928 : else
2929 0 : poPositiveNode.reset(poPoint);
2930 : }
2931 :
2932 : {
2933 : // Create a scope so that poMP can be initialized with goto
2934 : // above and label below.
2935 0 : auto poMP = std::make_unique<OGRMultiPoint>();
2936 0 : poMP->addGeometry(std::move(poNegativeNode));
2937 0 : poMP->addGeometry(std::move(poPositiveNode));
2938 :
2939 0 : return poMP;
2940 : }
2941 0 : nonode:;
2942 : }
2943 :
2944 : // Collect curveproperty.
2945 : const CPLXMLNode *psCurveProperty =
2946 1226 : FindBareXMLChild(psEdge, "curveProperty");
2947 1226 : if (psCurveProperty == nullptr)
2948 : {
2949 0 : ReportFailure("directedEdge: Failed to get curveProperty in Edge");
2950 0 : return nullptr;
2951 : }
2952 :
2953 : const CPLXMLNode *psCurve =
2954 1226 : FindBareXMLChild(psCurveProperty, "LineString");
2955 1226 : if (psCurve == nullptr)
2956 628 : psCurve = FindBareXMLChild(psCurveProperty, "Curve");
2957 1226 : if (psCurve == nullptr)
2958 : {
2959 0 : ReportFailure("directedEdge: Failed to get LineString or "
2960 : "Curve tag in curveProperty");
2961 0 : return nullptr;
2962 : }
2963 :
2964 : auto poLineStringBeforeCast = GML2OGRGeometry_XMLNode_Internal(
2965 : psCurve, pszId, nPseudoBoolGetSecondaryGeometryOption,
2966 2452 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache, true);
2967 2452 : if (poLineStringBeforeCast == nullptr ||
2968 1226 : wkbFlatten(poLineStringBeforeCast->getGeometryType()) !=
2969 : wkbLineString)
2970 : {
2971 0 : ReportFailure("Got %s geometry as Member instead of LINESTRING.",
2972 : poLineStringBeforeCast
2973 : ? poLineStringBeforeCast->getGeometryName()
2974 : : "NULL");
2975 0 : return nullptr;
2976 : }
2977 : auto poLineString = std::unique_ptr<OGRLineString>(
2978 2452 : poLineStringBeforeCast.release()->toLineString());
2979 :
2980 1226 : if (bGetSecondaryGeometry)
2981 : {
2982 : // Choose a point based on the orientation.
2983 0 : poNegativeNode = std::make_unique<OGRPoint>();
2984 0 : poPositiveNode = std::make_unique<OGRPoint>();
2985 0 : if (bEdgeOrientation == bOrientation)
2986 : {
2987 0 : poLineString->StartPoint(poNegativeNode.get());
2988 0 : poLineString->EndPoint(poPositiveNode.get());
2989 : }
2990 : else
2991 : {
2992 0 : poLineString->StartPoint(poPositiveNode.get());
2993 0 : poLineString->EndPoint(poNegativeNode.get());
2994 : }
2995 :
2996 0 : auto poMP = std::make_unique<OGRMultiPoint>();
2997 0 : poMP->addGeometry(std::move(poNegativeNode));
2998 0 : poMP->addGeometry(std::move(poPositiveNode));
2999 0 : return poMP;
3000 : }
3001 :
3002 : // correct orientation of the line string
3003 1226 : if (bEdgeOrientation != bOrientation)
3004 : {
3005 392 : poLineString->reversePoints();
3006 : }
3007 1226 : return poLineString;
3008 : }
3009 :
3010 : /* -------------------------------------------------------------------- */
3011 : /* TopoCurve */
3012 : /* -------------------------------------------------------------------- */
3013 593 : if (EQUAL(pszBaseGeometry, "TopoCurve"))
3014 : {
3015 390 : std::unique_ptr<OGRMultiLineString> poMLS;
3016 390 : std::unique_ptr<OGRMultiPoint> poMP;
3017 :
3018 390 : if (bGetSecondaryGeometry)
3019 0 : poMP = std::make_unique<OGRMultiPoint>();
3020 : else
3021 390 : poMLS = std::make_unique<OGRMultiLineString>();
3022 :
3023 : // Collect directedEdges.
3024 816 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
3025 426 : psChild = psChild->psNext)
3026 : {
3027 852 : if (psChild->eType == CXT_Element &&
3028 426 : EQUAL(BareGMLElement(psChild->pszValue), "directedEdge"))
3029 : {
3030 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
3031 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3032 426 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3033 426 : if (poGeom == nullptr)
3034 : {
3035 0 : ReportFailure("Failed to get geometry in directedEdge");
3036 0 : return nullptr;
3037 : }
3038 :
3039 : // Add the two points corresponding to the two nodes to poMP.
3040 426 : if (bGetSecondaryGeometry &&
3041 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
3042 : {
3043 : auto poMultiPoint = std::unique_ptr<OGRMultiPoint>(
3044 0 : poGeom.release()->toMultiPoint());
3045 :
3046 : // TODO: TopoCurve geometries with more than one
3047 : // directedEdge elements were not tested.
3048 0 : if (poMP->getNumGeometries() <= 0 ||
3049 0 : !(poMP->getGeometryRef(poMP->getNumGeometries() - 1)
3050 0 : ->Equals(poMultiPoint->getGeometryRef(0))))
3051 : {
3052 0 : poMP->addGeometry(poMultiPoint->getGeometryRef(0));
3053 : }
3054 0 : poMP->addGeometry(poMultiPoint->getGeometryRef(1));
3055 : }
3056 852 : else if (!bGetSecondaryGeometry &&
3057 426 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
3058 : {
3059 426 : poMLS->addGeometry(std::move(poGeom));
3060 : }
3061 : else
3062 : {
3063 0 : ReportFailure("Got %s geometry as Member instead of %s.",
3064 : poGeom->getGeometryName(),
3065 : bGetSecondaryGeometry ? "MULTIPOINT"
3066 : : "LINESTRING");
3067 0 : return nullptr;
3068 : }
3069 : }
3070 : }
3071 :
3072 390 : if (bGetSecondaryGeometry)
3073 0 : return poMP;
3074 :
3075 390 : return poMLS;
3076 : }
3077 :
3078 : /* -------------------------------------------------------------------- */
3079 : /* TopoSurface */
3080 : /* -------------------------------------------------------------------- */
3081 203 : if (EQUAL(pszBaseGeometry, "TopoSurface"))
3082 : {
3083 : /****************************************************************/
3084 : /* applying the FaceHoleNegative = false rules */
3085 : /* */
3086 : /* - each <TopoSurface> is expected to represent a MultiPolygon */
3087 : /* - each <Face> is expected to represent a distinct Polygon, */
3088 : /* this including any possible Interior Ring (holes); */
3089 : /* orientation="+/-" plays no role at all to identify "holes" */
3090 : /* - each <Edge> within a <Face> may indifferently represent */
3091 : /* an element of the Exterior or Interior Boundary; relative */
3092 : /* order of <Edges> is absolutely irrelevant. */
3093 : /****************************************************************/
3094 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
3095 : /* Developed for Faunalia (http://www.faunalia.it) */
3096 : /* with funding from Regione Toscana - */
3097 : /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE */
3098 : /****************************************************************/
3099 79 : if (!bFaceHoleNegative)
3100 : {
3101 59 : if (bGetSecondaryGeometry)
3102 0 : return nullptr;
3103 :
3104 : #ifndef HAVE_GEOS
3105 : static bool bWarningAlreadyEmitted = false;
3106 : if (!bWarningAlreadyEmitted)
3107 : {
3108 : ReportFailure(
3109 : "Interpreating that GML TopoSurface geometry requires GDAL "
3110 : "to be built with GEOS support. As a workaround, you can "
3111 : "try defining the GML_FACE_HOLE_NEGATIVE configuration "
3112 : "option to YES, so that the 'old' interpretation algorithm "
3113 : "is used. But be warned that the result might be "
3114 : "incorrect.");
3115 : bWarningAlreadyEmitted = true;
3116 : }
3117 : return nullptr;
3118 : #else
3119 118 : auto poTS = std::make_unique<OGRMultiPolygon>();
3120 :
3121 : // Collect directed faces.
3122 59 : for (const CPLXMLNode *psChild = psNode->psChild;
3123 173 : psChild != nullptr; psChild = psChild->psNext)
3124 : {
3125 228 : if (psChild->eType == CXT_Element &&
3126 114 : EQUAL(BareGMLElement(psChild->pszValue), "directedFace"))
3127 : {
3128 : // Collect next face (psChild->psChild).
3129 114 : const CPLXMLNode *psFaceChild = GetChildElement(psChild);
3130 :
3131 0 : while (
3132 228 : psFaceChild != nullptr &&
3133 114 : !(psFaceChild->eType == CXT_Element &&
3134 114 : EQUAL(BareGMLElement(psFaceChild->pszValue), "Face")))
3135 0 : psFaceChild = psFaceChild->psNext;
3136 :
3137 114 : if (psFaceChild == nullptr)
3138 0 : continue;
3139 :
3140 : auto poCollectedGeom =
3141 114 : std::make_unique<OGRMultiLineString>();
3142 :
3143 : // Collect directed edges of the face.
3144 114 : for (const CPLXMLNode *psDirectedEdgeChild =
3145 : psFaceChild->psChild;
3146 856 : psDirectedEdgeChild != nullptr;
3147 742 : psDirectedEdgeChild = psDirectedEdgeChild->psNext)
3148 : {
3149 1370 : if (psDirectedEdgeChild->eType == CXT_Element &&
3150 628 : EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),
3151 : "directedEdge"))
3152 : {
3153 : auto poEdgeGeom = GML2OGRGeometry_XMLNode_Internal(
3154 : psDirectedEdgeChild, pszId,
3155 : nPseudoBoolGetSecondaryGeometryOption,
3156 : nRecLevel + 1, nSRSDimension, pszSRSName,
3157 628 : oSRSCache, true);
3158 :
3159 1256 : if (poEdgeGeom == nullptr ||
3160 628 : wkbFlatten(poEdgeGeom->getGeometryType()) !=
3161 : wkbLineString)
3162 : {
3163 0 : ReportFailure(
3164 : "Failed to get geometry in directedEdge");
3165 0 : return nullptr;
3166 : }
3167 :
3168 628 : poCollectedGeom->addGeometry(std::move(poEdgeGeom));
3169 : }
3170 : }
3171 :
3172 : auto poFaceCollectionGeom = std::unique_ptr<OGRGeometry>(
3173 114 : poCollectedGeom->Polygonize());
3174 114 : if (poFaceCollectionGeom == nullptr)
3175 : {
3176 0 : ReportFailure("Failed to assemble Edges in Face");
3177 0 : return nullptr;
3178 : }
3179 :
3180 : auto poFaceGeom =
3181 114 : GML2FaceExtRing(poFaceCollectionGeom.get());
3182 :
3183 114 : if (poFaceGeom == nullptr)
3184 : {
3185 0 : ReportFailure("Failed to build Polygon for Face");
3186 0 : return nullptr;
3187 : }
3188 : else
3189 : {
3190 114 : int iCount = poTS->getNumGeometries();
3191 114 : if (iCount == 0)
3192 : {
3193 : // Inserting the first Polygon.
3194 59 : poTS->addGeometry(std::move(poFaceGeom));
3195 : }
3196 : else
3197 : {
3198 : // Using Union to add the current Polygon.
3199 : auto poUnion = std::unique_ptr<OGRGeometry>(
3200 55 : poTS->Union(poFaceGeom.get()));
3201 55 : if (poUnion == nullptr)
3202 : {
3203 0 : ReportFailure("Failed Union for TopoSurface");
3204 0 : return nullptr;
3205 : }
3206 55 : if (wkbFlatten(poUnion->getGeometryType()) ==
3207 : wkbPolygon)
3208 : {
3209 : // Forcing to be a MultiPolygon.
3210 34 : poTS = std::make_unique<OGRMultiPolygon>();
3211 34 : poTS->addGeometry(std::move(poUnion));
3212 : }
3213 21 : else if (wkbFlatten(poUnion->getGeometryType()) ==
3214 : wkbMultiPolygon)
3215 : {
3216 21 : poTS.reset(poUnion.release()->toMultiPolygon());
3217 : }
3218 : else
3219 : {
3220 0 : ReportFailure(
3221 : "Unexpected geometry type resulting "
3222 : "from Union for TopoSurface");
3223 0 : return nullptr;
3224 : }
3225 : }
3226 : }
3227 : }
3228 : }
3229 :
3230 59 : return poTS;
3231 : #endif // HAVE_GEOS
3232 : }
3233 :
3234 : /****************************************************************/
3235 : /* applying the FaceHoleNegative = true rules */
3236 : /* */
3237 : /* - each <TopoSurface> is expected to represent a MultiPolygon */
3238 : /* - any <Face> declaring orientation="+" is expected to */
3239 : /* represent an Exterior Ring (no holes are allowed) */
3240 : /* - any <Face> declaring orientation="-" is expected to */
3241 : /* represent an Interior Ring (hole) belonging to the latest */
3242 : /* Exterior Ring. */
3243 : /* - <Edges> within the same <Face> are expected to be */
3244 : /* arranged in geometrically adjacent and consecutive */
3245 : /* sequence. */
3246 : /****************************************************************/
3247 20 : if (bGetSecondaryGeometry)
3248 0 : return nullptr;
3249 20 : bool bFaceOrientation = true;
3250 40 : auto poTS = std::make_unique<OGRPolygon>();
3251 :
3252 : // Collect directed faces.
3253 60 : for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
3254 40 : psChild = psChild->psNext)
3255 : {
3256 78 : if (psChild->eType == CXT_Element &&
3257 38 : EQUAL(BareGMLElement(psChild->pszValue), "directedFace"))
3258 : {
3259 38 : bFaceOrientation = GetElementOrientation(psChild);
3260 :
3261 : // Collect next face (psChild->psChild).
3262 38 : const CPLXMLNode *psFaceChild = GetChildElement(psChild);
3263 76 : while (psFaceChild != nullptr &&
3264 38 : !EQUAL(BareGMLElement(psFaceChild->pszValue), "Face"))
3265 0 : psFaceChild = psFaceChild->psNext;
3266 :
3267 38 : if (psFaceChild == nullptr)
3268 0 : continue;
3269 :
3270 38 : auto poFaceGeom = std::make_unique<OGRLinearRing>();
3271 :
3272 : // Collect directed edges of the face.
3273 38 : for (const CPLXMLNode *psDirectedEdgeChild =
3274 : psFaceChild->psChild;
3275 246 : psDirectedEdgeChild != nullptr;
3276 208 : psDirectedEdgeChild = psDirectedEdgeChild->psNext)
3277 : {
3278 380 : if (psDirectedEdgeChild->eType == CXT_Element &&
3279 172 : EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),
3280 : "directedEdge"))
3281 : {
3282 : auto poEdgeGeom = GML2OGRGeometry_XMLNode_Internal(
3283 : psDirectedEdgeChild, pszId,
3284 : nPseudoBoolGetSecondaryGeometryOption,
3285 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache,
3286 172 : true, bFaceOrientation);
3287 :
3288 344 : if (poEdgeGeom == nullptr ||
3289 172 : wkbFlatten(poEdgeGeom->getGeometryType()) !=
3290 : wkbLineString)
3291 : {
3292 0 : ReportFailure(
3293 : "Failed to get geometry in directedEdge");
3294 0 : return nullptr;
3295 : }
3296 :
3297 : auto poEdgeGeomLS = std::unique_ptr<OGRLineString>(
3298 344 : poEdgeGeom.release()->toLineString());
3299 172 : if (!bFaceOrientation)
3300 : {
3301 30 : OGRLineString *poLS = poEdgeGeomLS.get();
3302 30 : OGRLineString *poAddLS = poFaceGeom.get();
3303 :
3304 : // TODO(schwehr): Use AlmostEqual.
3305 30 : const double epsilon = 1.0e-14;
3306 30 : if (poAddLS->getNumPoints() < 2)
3307 : {
3308 : // Skip it.
3309 : }
3310 18 : else if (poLS->getNumPoints() > 0 &&
3311 36 : fabs(poLS->getX(poLS->getNumPoints() - 1) -
3312 18 : poAddLS->getX(0)) < epsilon &&
3313 36 : fabs(poLS->getY(poLS->getNumPoints() - 1) -
3314 54 : poAddLS->getY(0)) < epsilon &&
3315 18 : fabs(poLS->getZ(poLS->getNumPoints() - 1) -
3316 18 : poAddLS->getZ(0)) < epsilon)
3317 : {
3318 : // Skip the first point of the new linestring to
3319 : // avoid invalidate duplicate points.
3320 18 : poLS->addSubLineString(poAddLS, 1);
3321 : }
3322 : else
3323 : {
3324 : // Add the whole new line string.
3325 0 : poLS->addSubLineString(poAddLS);
3326 : }
3327 30 : poFaceGeom->empty();
3328 : }
3329 : // TODO(schwehr): Suspicious that poLS overwritten
3330 : // without else.
3331 172 : OGRLineString *poLS = poFaceGeom.get();
3332 172 : OGRLineString *poAddLS = poEdgeGeomLS.get();
3333 172 : if (poAddLS->getNumPoints() < 2)
3334 : {
3335 : // Skip it.
3336 : }
3337 172 : else if (poLS->getNumPoints() > 0 &&
3338 232 : fabs(poLS->getX(poLS->getNumPoints() - 1) -
3339 116 : poAddLS->getX(0)) < 1e-14 &&
3340 232 : fabs(poLS->getY(poLS->getNumPoints() - 1) -
3341 404 : poAddLS->getY(0)) < 1e-14 &&
3342 116 : fabs(poLS->getZ(poLS->getNumPoints() - 1) -
3343 116 : poAddLS->getZ(0)) < 1e-14)
3344 : {
3345 : // Skip the first point of the new linestring to
3346 : // avoid invalidate duplicate points.
3347 116 : poLS->addSubLineString(poAddLS, 1);
3348 : }
3349 : else
3350 : {
3351 : // Add the whole new line string.
3352 56 : poLS->addSubLineString(poAddLS);
3353 : }
3354 : }
3355 : }
3356 :
3357 : // if( poFaceGeom == NULL )
3358 : // {
3359 : // ReportFailure(
3360 : // "Failed to get Face geometry in directedFace"
3361 : // );
3362 : // delete poFaceGeom;
3363 : // return NULL;
3364 : // }
3365 :
3366 38 : poTS->addRing(std::move(poFaceGeom));
3367 : }
3368 : }
3369 :
3370 : // if( poTS == NULL )
3371 : // {
3372 : // ReportFailure(
3373 : // "Failed to get TopoSurface geometry" );
3374 : // delete poTS;
3375 : // return NULL;
3376 : // }
3377 :
3378 20 : return poTS;
3379 : }
3380 :
3381 : /* -------------------------------------------------------------------- */
3382 : /* Surface */
3383 : /* -------------------------------------------------------------------- */
3384 124 : if (EQUAL(pszBaseGeometry, "Surface") ||
3385 81 : EQUAL(pszBaseGeometry, "ElevatedSurface") /* AIXM */)
3386 : {
3387 : // Find outer ring.
3388 45 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "patches");
3389 45 : if (psChild == nullptr)
3390 3 : psChild = FindBareXMLChild(psNode, "polygonPatches");
3391 45 : if (psChild == nullptr)
3392 3 : psChild = FindBareXMLChild(psNode, "trianglePatches");
3393 :
3394 45 : psChild = GetChildElement(psChild);
3395 45 : if (psChild == nullptr)
3396 : {
3397 : // <gml:Surface/> and <gml:Surface><gml:patches/></gml:Surface> are
3398 : // valid GML.
3399 3 : return std::make_unique<OGRPolygon>();
3400 : }
3401 :
3402 42 : OGRMultiSurface *poMSPtr = nullptr;
3403 42 : std::unique_ptr<OGRGeometry> poResultPoly;
3404 42 : std::unique_ptr<OGRGeometry> poResultTri;
3405 42 : OGRTriangulatedSurface *poTINPtr = nullptr;
3406 90 : for (; psChild != nullptr; psChild = psChild->psNext)
3407 : {
3408 96 : if (psChild->eType == CXT_Element &&
3409 48 : (EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch") ||
3410 5 : EQUAL(BareGMLElement(psChild->pszValue), "Rectangle")))
3411 : {
3412 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
3413 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3414 44 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3415 44 : if (poGeom == nullptr)
3416 : {
3417 0 : return nullptr;
3418 : }
3419 :
3420 : const OGRwkbGeometryType eGeomType =
3421 44 : wkbFlatten(poGeom->getGeometryType());
3422 :
3423 44 : if (poResultPoly == nullptr)
3424 40 : poResultPoly = std::move(poGeom);
3425 : else
3426 : {
3427 4 : if (poMSPtr == nullptr)
3428 : {
3429 0 : std::unique_ptr<OGRMultiSurface> poMS;
3430 3 : if (wkbFlatten(poResultPoly->getGeometryType()) ==
3431 3 : wkbPolygon &&
3432 : eGeomType == wkbPolygon)
3433 2 : poMS = std::make_unique<OGRMultiPolygon>();
3434 : else
3435 1 : poMS = std::make_unique<OGRMultiSurface>();
3436 : OGRErr eErr =
3437 3 : poMS->addGeometry(std::move(poResultPoly));
3438 3 : CPL_IGNORE_RET_VAL(eErr);
3439 3 : CPLAssert(eErr == OGRERR_NONE);
3440 3 : poResultPoly = std::move(poMS);
3441 3 : poMSPtr = cpl::down_cast<OGRMultiSurface *>(
3442 : poResultPoly.get());
3443 : }
3444 2 : else if (eGeomType != wkbPolygon &&
3445 1 : wkbFlatten(poResultPoly->getGeometryType()) ==
3446 : wkbMultiPolygon)
3447 : {
3448 : OGRMultiPolygon *poMultiPoly =
3449 1 : poResultPoly.release()->toMultiPolygon();
3450 1 : poResultPoly.reset(
3451 1 : OGRMultiPolygon::CastToMultiSurface(poMultiPoly));
3452 1 : poMSPtr = cpl::down_cast<OGRMultiSurface *>(
3453 : poResultPoly.get());
3454 : }
3455 4 : OGRErr eErr = poMSPtr->addGeometry(std::move(poGeom));
3456 4 : CPL_IGNORE_RET_VAL(eErr);
3457 4 : CPLAssert(eErr == OGRERR_NONE);
3458 : }
3459 : }
3460 8 : else if (psChild->eType == CXT_Element &&
3461 4 : EQUAL(BareGMLElement(psChild->pszValue), "Triangle"))
3462 : {
3463 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
3464 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3465 3 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3466 3 : if (poGeom == nullptr)
3467 : {
3468 0 : return nullptr;
3469 : }
3470 :
3471 3 : if (poResultTri == nullptr)
3472 2 : poResultTri = std::move(poGeom);
3473 : else
3474 : {
3475 1 : if (poTINPtr == nullptr)
3476 : {
3477 1 : auto poTIN = std::make_unique<OGRTriangulatedSurface>();
3478 : OGRErr eErr =
3479 1 : poTIN->addGeometry(std::move(poResultTri));
3480 1 : CPL_IGNORE_RET_VAL(eErr);
3481 1 : CPLAssert(eErr == OGRERR_NONE);
3482 1 : poResultTri = std::move(poTIN);
3483 1 : poTINPtr = cpl::down_cast<OGRTriangulatedSurface *>(
3484 : poResultTri.get());
3485 : }
3486 1 : OGRErr eErr = poTINPtr->addGeometry(std::move(poGeom));
3487 1 : CPL_IGNORE_RET_VAL(eErr);
3488 1 : CPLAssert(eErr == OGRERR_NONE);
3489 : }
3490 : }
3491 : }
3492 :
3493 42 : if (poResultTri == nullptr && poResultPoly == nullptr)
3494 1 : return nullptr;
3495 :
3496 41 : if (poResultTri == nullptr)
3497 39 : return poResultPoly;
3498 2 : else if (poResultPoly == nullptr)
3499 1 : return poResultTri;
3500 : else
3501 : {
3502 2 : auto poGC = std::make_unique<OGRGeometryCollection>();
3503 1 : poGC->addGeometry(std::move(poResultTri));
3504 1 : poGC->addGeometry(std::move(poResultPoly));
3505 1 : return poGC;
3506 : }
3507 : }
3508 :
3509 : /* -------------------------------------------------------------------- */
3510 : /* TriangulatedSurface */
3511 : /* -------------------------------------------------------------------- */
3512 79 : if (EQUAL(pszBaseGeometry, "TriangulatedSurface") ||
3513 75 : EQUAL(pszBaseGeometry, "Tin"))
3514 : {
3515 : // Find trianglePatches.
3516 5 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "trianglePatches");
3517 5 : if (psChild == nullptr)
3518 4 : psChild = FindBareXMLChild(psNode, "patches");
3519 :
3520 5 : psChild = GetChildElement(psChild);
3521 5 : if (psChild == nullptr)
3522 : {
3523 1 : ReportFailure("Missing <trianglePatches> for %s.", pszBaseGeometry);
3524 1 : return nullptr;
3525 : }
3526 :
3527 8 : auto poTIN = std::make_unique<OGRTriangulatedSurface>();
3528 9 : for (; psChild != nullptr; psChild = psChild->psNext)
3529 : {
3530 12 : if (psChild->eType == CXT_Element &&
3531 6 : EQUAL(BareGMLElement(psChild->pszValue), "Triangle"))
3532 : {
3533 : auto poTriangle = GML2OGRGeometry_XMLNode_Internal(
3534 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3535 6 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3536 6 : if (poTriangle == nullptr)
3537 : {
3538 1 : return nullptr;
3539 : }
3540 : else
3541 : {
3542 5 : poTIN->addGeometry(std::move(poTriangle));
3543 : }
3544 : }
3545 : }
3546 :
3547 3 : return poTIN;
3548 : }
3549 :
3550 : /* -------------------------------------------------------------------- */
3551 : /* PolyhedralSurface */
3552 : /* -------------------------------------------------------------------- */
3553 74 : if (EQUAL(pszBaseGeometry, "PolyhedralSurface"))
3554 : {
3555 : // Find polygonPatches.
3556 11 : const CPLXMLNode *psParent = FindBareXMLChild(psNode, "polygonPatches");
3557 11 : if (psParent == nullptr)
3558 : {
3559 2 : if (GetChildElement(psNode) == nullptr)
3560 : {
3561 : // This is empty PolyhedralSurface.
3562 1 : return std::make_unique<OGRPolyhedralSurface>();
3563 : }
3564 : else
3565 : {
3566 1 : ReportFailure("Missing <polygonPatches> for %s.",
3567 : pszBaseGeometry);
3568 1 : return nullptr;
3569 : }
3570 : }
3571 :
3572 9 : const CPLXMLNode *psChild = GetChildElement(psParent);
3573 9 : if (psChild == nullptr)
3574 : {
3575 : // This is empty PolyhedralSurface.
3576 1 : return std::make_unique<OGRPolyhedralSurface>();
3577 : }
3578 8 : else if (!EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch"))
3579 : {
3580 1 : ReportFailure("Missing <PolygonPatch> for %s.", pszBaseGeometry);
3581 1 : return nullptr;
3582 : }
3583 :
3584 : // Each psParent has the tags corresponding to <gml:polygonPatches>
3585 : // Each psChild has the tags corresponding to <gml:PolygonPatch>
3586 : // Each PolygonPatch has a set of polygons enclosed in a
3587 : // OGRPolyhedralSurface.
3588 14 : auto poGC = std::make_unique<OGRGeometryCollection>();
3589 15 : for (; psParent != nullptr; psParent = psParent->psNext)
3590 : {
3591 9 : psChild = GetChildElement(psParent);
3592 9 : if (psChild == nullptr)
3593 1 : continue;
3594 8 : auto poPS = std::make_unique<OGRPolyhedralSurface>();
3595 23 : for (; psChild != nullptr; psChild = psChild->psNext)
3596 : {
3597 31 : if (psChild->eType == CXT_Element &&
3598 15 : EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch"))
3599 : {
3600 : auto poPolygon = GML2OGRGeometry_XMLNode_Internal(
3601 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3602 15 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3603 15 : if (poPolygon == nullptr)
3604 : {
3605 1 : ReportFailure("Wrong geometry type for %s.",
3606 : pszBaseGeometry);
3607 1 : return nullptr;
3608 : }
3609 :
3610 14 : else if (wkbFlatten(poPolygon->getGeometryType()) ==
3611 : wkbPolygon)
3612 : {
3613 13 : poPS->addGeometry(std::move(poPolygon));
3614 : }
3615 1 : else if (wkbFlatten(poPolygon->getGeometryType()) ==
3616 : wkbCurvePolygon)
3617 : {
3618 1 : poPS->addGeometryDirectly(
3619 : OGRGeometryFactory::forceToPolygon(
3620 : poPolygon.release()));
3621 : }
3622 : else
3623 : {
3624 0 : ReportFailure("Wrong geometry type for %s.",
3625 : pszBaseGeometry);
3626 0 : return nullptr;
3627 : }
3628 : }
3629 : }
3630 7 : poGC->addGeometry(std::move(poPS));
3631 : }
3632 :
3633 6 : if (poGC->getNumGeometries() == 0)
3634 : {
3635 0 : return nullptr;
3636 : }
3637 6 : else if (poGC->getNumGeometries() == 1)
3638 : {
3639 : auto poResult =
3640 10 : std::unique_ptr<OGRGeometry>(poGC->getGeometryRef(0));
3641 5 : poGC->removeGeometry(0, FALSE);
3642 5 : return poResult;
3643 : }
3644 : else
3645 : {
3646 1 : return poGC;
3647 : }
3648 : }
3649 :
3650 : /* -------------------------------------------------------------------- */
3651 : /* Solid */
3652 : /* -------------------------------------------------------------------- */
3653 63 : if (EQUAL(pszBaseGeometry, "Solid"))
3654 : {
3655 21 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "interior");
3656 21 : if (psChild != nullptr)
3657 : {
3658 : static bool bWarnedOnce = false;
3659 1 : if (!bWarnedOnce)
3660 : {
3661 1 : ReportWarning("<interior> elements of <Solid> are ignored");
3662 1 : bWarnedOnce = true;
3663 : }
3664 : }
3665 :
3666 : // Find exterior element.
3667 21 : psChild = FindBareXMLChild(psNode, "exterior");
3668 :
3669 21 : if (nSRSDimension == 0)
3670 21 : nSRSDimension = 3;
3671 :
3672 21 : psChild = GetChildElement(psChild);
3673 21 : if (psChild == nullptr)
3674 : {
3675 : // <gml:Solid/> and <gml:Solid><gml:exterior/></gml:Solid> are valid
3676 : // GML.
3677 3 : return std::make_unique<OGRPolyhedralSurface>();
3678 : }
3679 :
3680 29 : if (EQUAL(BareGMLElement(psChild->pszValue), "CompositeSurface") ||
3681 11 : EQUAL(BareGMLElement(psChild->pszValue), "Shell"))
3682 : {
3683 22 : auto poPS = std::make_unique<OGRPolyhedralSurface>();
3684 :
3685 : // Iterate over children.
3686 86 : for (psChild = psChild->psChild; psChild != nullptr;
3687 75 : psChild = psChild->psNext)
3688 : {
3689 : const char *pszMemberElement =
3690 75 : BareGMLElement(psChild->pszValue);
3691 75 : if (psChild->eType == CXT_Element &&
3692 73 : (EQUAL(pszMemberElement, "polygonMember") ||
3693 73 : EQUAL(pszMemberElement, "surfaceMember")))
3694 : {
3695 73 : const CPLXMLNode *psSurfaceChild = GetChildElement(psChild);
3696 :
3697 73 : if (psSurfaceChild != nullptr)
3698 : {
3699 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
3700 : psSurfaceChild, pszId,
3701 : nPseudoBoolGetSecondaryGeometryOption,
3702 : nRecLevel + 1, nSRSDimension, pszSRSName,
3703 146 : oSRSCache);
3704 146 : if (poGeom != nullptr &&
3705 73 : wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
3706 : {
3707 73 : poPS->addGeometry(std::move(poGeom));
3708 : }
3709 : }
3710 : }
3711 : }
3712 11 : return poPS;
3713 : }
3714 :
3715 : // Get the geometry inside <exterior>.
3716 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
3717 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3718 14 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3719 7 : if (poGeom == nullptr)
3720 : {
3721 6 : ReportFailure("Invalid exterior element");
3722 6 : return nullptr;
3723 : }
3724 :
3725 1 : return poGeom;
3726 : }
3727 :
3728 : /* -------------------------------------------------------------------- */
3729 : /* OrientableCurve */
3730 : /* -------------------------------------------------------------------- */
3731 42 : if (EQUAL(pszBaseGeometry, "OrientableCurve"))
3732 : {
3733 : // Find baseCurve.
3734 7 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "baseCurve");
3735 :
3736 7 : psChild = GetChildElement(psChild);
3737 7 : if (psChild == nullptr)
3738 : {
3739 3 : ReportFailure("Missing <baseCurve> for OrientableCurve.");
3740 3 : return nullptr;
3741 : }
3742 :
3743 : auto poGeom = GML2OGRGeometry_XMLNode_Internal(
3744 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3745 8 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3746 4 : if (!poGeom || !OGR_GT_IsCurve(poGeom->getGeometryType()))
3747 : {
3748 2 : ReportFailure("baseCurve of OrientableCurve is not a curve.");
3749 2 : return nullptr;
3750 : }
3751 2 : if (!GetElementOrientation(psNode))
3752 : {
3753 1 : poGeom->toCurve()->reversePoints();
3754 : }
3755 2 : return poGeom;
3756 : }
3757 :
3758 : /* -------------------------------------------------------------------- */
3759 : /* OrientableSurface */
3760 : /* -------------------------------------------------------------------- */
3761 35 : if (EQUAL(pszBaseGeometry, "OrientableSurface"))
3762 : {
3763 : // Find baseSurface.
3764 5 : const CPLXMLNode *psChild = FindBareXMLChild(psNode, "baseSurface");
3765 :
3766 5 : psChild = GetChildElement(psChild);
3767 5 : if (psChild == nullptr)
3768 : {
3769 3 : ReportFailure("Missing <baseSurface> for OrientableSurface.");
3770 3 : return nullptr;
3771 : }
3772 :
3773 : return GML2OGRGeometry_XMLNode_Internal(
3774 : psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
3775 2 : nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
3776 : }
3777 :
3778 : /* -------------------------------------------------------------------- */
3779 : /* SimplePolygon, SimpleRectangle, SimpleTriangle */
3780 : /* (GML 3.3 compact encoding) */
3781 : /* -------------------------------------------------------------------- */
3782 30 : if (EQUAL(pszBaseGeometry, "SimplePolygon") ||
3783 26 : EQUAL(pszBaseGeometry, "SimpleRectangle"))
3784 : {
3785 10 : auto poRing = std::make_unique<OGRLinearRing>();
3786 :
3787 5 : if (!ParseGMLCoordinates(psNode, poRing.get(), nSRSDimension))
3788 : {
3789 2 : return nullptr;
3790 : }
3791 :
3792 3 : poRing->closeRings();
3793 :
3794 6 : auto poPolygon = std::make_unique<OGRPolygon>();
3795 3 : poPolygon->addRing(std::move(poRing));
3796 3 : return poPolygon;
3797 : }
3798 :
3799 25 : if (EQUAL(pszBaseGeometry, "SimpleTriangle"))
3800 : {
3801 2 : auto poRing = std::make_unique<OGRLinearRing>();
3802 :
3803 1 : if (!ParseGMLCoordinates(psNode, poRing.get(), nSRSDimension))
3804 : {
3805 0 : return nullptr;
3806 : }
3807 :
3808 1 : poRing->closeRings();
3809 :
3810 2 : auto poTriangle = std::make_unique<OGRTriangle>();
3811 1 : poTriangle->addRing(std::move(poRing));
3812 1 : return poTriangle;
3813 : }
3814 :
3815 : /* -------------------------------------------------------------------- */
3816 : /* SimpleMultiPoint (GML 3.3 compact encoding) */
3817 : /* -------------------------------------------------------------------- */
3818 24 : if (EQUAL(pszBaseGeometry, "SimpleMultiPoint"))
3819 : {
3820 8 : auto poLS = std::make_unique<OGRLineString>();
3821 :
3822 4 : if (!ParseGMLCoordinates(psNode, poLS.get(), nSRSDimension))
3823 : {
3824 2 : return nullptr;
3825 : }
3826 :
3827 4 : auto poMP = std::make_unique<OGRMultiPoint>();
3828 2 : int nPoints = poLS->getNumPoints();
3829 4 : for (int i = 0; i < nPoints; i++)
3830 : {
3831 2 : auto poPoint = std::make_unique<OGRPoint>();
3832 2 : poLS->getPoint(i, poPoint.get());
3833 2 : poMP->addGeometry(std::move(poPoint));
3834 : }
3835 2 : return poMP;
3836 : }
3837 :
3838 20 : if (strcmp(pszBaseGeometry, "null") == 0)
3839 : {
3840 1 : return nullptr;
3841 : }
3842 :
3843 19 : ReportFailure("Unrecognized geometry type <%s>.", pszBaseGeometry);
3844 :
3845 19 : return nullptr;
3846 : }
3847 :
3848 : /************************************************************************/
3849 : /* OGR_G_CreateFromGMLTree() */
3850 : /************************************************************************/
3851 :
3852 : /** Create geometry from GML */
3853 250 : OGRGeometryH OGR_G_CreateFromGMLTree(const CPLXMLNode *psTree)
3854 :
3855 : {
3856 250 : return OGRGeometry::ToHandle(GML2OGRGeometry_XMLNode(psTree, -1));
3857 : }
3858 :
3859 : /************************************************************************/
3860 : /* OGR_G_CreateFromGML() */
3861 : /************************************************************************/
3862 :
3863 : /**
3864 : * \brief Create geometry from GML.
3865 : *
3866 : * This method translates a fragment of GML containing only the geometry
3867 : * portion into a corresponding OGRGeometry. There are many limitations
3868 : * on the forms of GML geometries supported by this parser, but they are
3869 : * too numerous to list here.
3870 : *
3871 : * The following GML2 elements are parsed : Point, LineString, Polygon,
3872 : * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry.
3873 : *
3874 : * The following GML3 elements are parsed : Surface,
3875 : * MultiSurface, PolygonPatch, Triangle, Rectangle, Curve, MultiCurve,
3876 : * CompositeCurve, LineStringSegment, Arc, Circle, CompositeSurface,
3877 : * Shell, OrientableSurface, Solid, Tin, TriangulatedSurface.
3878 : *
3879 : * Arc and Circle elements are returned as curves by default. Stroking to
3880 : * linestrings can be done with
3881 : * OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)), NULL).
3882 : * A 4 degrees step is used by default, unless the user
3883 : * has overridden the value with the OGR_ARC_STEPSIZE configuration variable.
3884 : *
3885 : * The C++ method OGRGeometryFactory::createFromGML() is the same as
3886 : * this function.
3887 : *
3888 : * @param pszGML The GML fragment for the geometry.
3889 : *
3890 : * @return a geometry on success, or NULL on error.
3891 : *
3892 : * @see OGR_G_ForceTo()
3893 : * @see OGR_GT_GetLinear()
3894 : * @see OGR_G_GetGeometryType()
3895 : */
3896 :
3897 848 : OGRGeometryH OGR_G_CreateFromGML(const char *pszGML)
3898 :
3899 : {
3900 848 : if (pszGML == nullptr || strlen(pszGML) == 0)
3901 : {
3902 0 : CPLError(CE_Failure, CPLE_AppDefined,
3903 : "GML Geometry is empty in OGR_G_CreateFromGML().");
3904 0 : return nullptr;
3905 : }
3906 :
3907 : /* -------------------------------------------------------------------- */
3908 : /* Try to parse the XML snippet using the MiniXML API. If this */
3909 : /* fails, we assume the minixml api has already posted a CPL */
3910 : /* error, and just return NULL. */
3911 : /* -------------------------------------------------------------------- */
3912 848 : CPLXMLNode *psGML = CPLParseXMLString(pszGML);
3913 :
3914 848 : if (psGML == nullptr)
3915 3 : return nullptr;
3916 :
3917 : /* -------------------------------------------------------------------- */
3918 : /* Convert geometry recursively. */
3919 : /* -------------------------------------------------------------------- */
3920 : // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer()
3921 : // and GMLReader::GMLReader().
3922 : const bool bFaceHoleNegative =
3923 845 : CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO"));
3924 845 : OGRGeometry *poGeometry = GML2OGRGeometry_XMLNode(psGML, -1, 0, 0, false,
3925 : true, bFaceHoleNegative);
3926 :
3927 845 : CPLDestroyXMLNode(psGML);
3928 :
3929 845 : return OGRGeometry::ToHandle(poGeometry);
3930 : }
|