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