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