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