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