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