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