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