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