1 /******************************************************************************
2 * $Id: gml2ogrgeometry.cpp 14768 2008-06-25 19:37:42Z warmerdam $
3 *
4 * Project: GML Reader
5 * Purpose: Code to translate between GML and OGR geometry forms.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2002, Frank Warmerdam
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 "../port/cpl_minixml.h"
42 #include "../ogr/ogr_geometry.h"
43 #include "../ogr/ogr_api.h"
44 #include "../port/cpl_error.h"
45 #include "../port/cpl_string.h"
46 #include <ctype.h>
47
48 /************************************************************************/
49 /* BareGMLElement() */
50 /* */
51 /* Returns the passed string with any namespace prefix */
52 /* stripped off. */
53 /************************************************************************/
54
BareGMLElement(const char * pszInput)55 static const char *BareGMLElement( const char *pszInput )
56
57 {
58 const char *pszReturn;
59
60 pszReturn = strchr( pszInput, ':' );
61 if( pszReturn == NULL )
62 pszReturn = pszInput;
63 else
64 pszReturn++;
65
66 return pszReturn;
67 }
68
69 /************************************************************************/
70 /* FindBareXMLChild() */
71 /* */
72 /* Find a child node with the indicated "bare" name, that is */
73 /* after any namespace qualifiers have been stripped off. */
74 /************************************************************************/
75
FindBareXMLChild(CPLXMLNode * psParent,const char * pszBareName)76 static CPLXMLNode *FindBareXMLChild( CPLXMLNode *psParent,
77 const char *pszBareName )
78
79 {
80 CPLXMLNode *psCandidate = psParent->psChild;
81
82 while( psCandidate != NULL )
83 {
84 if( psCandidate->eType == CXT_Element
85 && EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName) )
86 return psCandidate;
87
88 psCandidate = psCandidate->psNext;
89 }
90
91 return NULL;
92 }
93
94 /************************************************************************/
95 /* GetElementText() */
96 /************************************************************************/
97
GetElementText(CPLXMLNode * psElement)98 static const char *GetElementText( CPLXMLNode *psElement )
99
100 {
101 if( psElement == NULL )
102 return NULL;
103
104 CPLXMLNode *psChild = psElement->psChild;
105
106 while( psChild != NULL )
107 {
108 if( psChild->eType == CXT_Text )
109 return psChild->pszValue;
110
111 psChild = psChild->psNext;
112 }
113
114 return NULL;
115 }
116
117 /************************************************************************/
118 /* AddPoint() */
119 /* */
120 /* Add a point to the passed geometry. */
121 /************************************************************************/
122
AddPoint(OGRGeometry * poGeometry,double dfX,double dfY,double dfZ,int nDimension)123 static int AddPoint( OGRGeometry *poGeometry,
124 double dfX, double dfY, double dfZ, int nDimension )
125
126 {
127 if( poGeometry->getGeometryType() == wkbPoint
128 || poGeometry->getGeometryType() == wkbPoint25D )
129 {
130 OGRPoint *poPoint = (OGRPoint *) poGeometry;
131
132 if( poPoint->getX() != 0.0 || poPoint->getY() != 0.0 )
133 {
134 CPLError( CE_Failure, CPLE_AppDefined,
135 "More than one coordinate for <Point> element.");
136 return FALSE;
137 }
138
139 poPoint->setX( dfX );
140 poPoint->setY( dfY );
141 if( nDimension == 3 )
142 poPoint->setZ( dfZ );
143
144 return TRUE;
145 }
146
147 else if( poGeometry->getGeometryType() == wkbLineString
148 || poGeometry->getGeometryType() == wkbLineString25D )
149 {
150 if( nDimension == 3 )
151 ((OGRLineString *) poGeometry)->addPoint( dfX, dfY, dfZ );
152 else
153 ((OGRLineString *) poGeometry)->addPoint( dfX, dfY );
154
155 return TRUE;
156 }
157
158 else
159 {
160 CPLAssert( FALSE );
161 return FALSE;
162 }
163 }
164
165 /************************************************************************/
166 /* ParseGMLCoordinates() */
167 /************************************************************************/
168
ParseGMLCoordinates(CPLXMLNode * psGeomNode,OGRGeometry * poGeometry)169 int ParseGMLCoordinates( CPLXMLNode *psGeomNode, OGRGeometry *poGeometry )
170
171 {
172 CPLXMLNode *psCoordinates = FindBareXMLChild( psGeomNode, "coordinates" );
173 int iCoord = 0;
174
175 /* -------------------------------------------------------------------- */
176 /* Handle <coordinates> case. */
177 /* -------------------------------------------------------------------- */
178 if( psCoordinates != NULL )
179 {
180 const char *pszCoordString = GetElementText( psCoordinates );
181
182 if( pszCoordString == NULL )
183 {
184 CPLError( CE_Failure, CPLE_AppDefined,
185 "<coordinates> element missing value." );
186 return FALSE;
187 }
188
189 while( *pszCoordString != '\0' )
190 {
191 double dfX, dfY, dfZ = 0.0;
192 int nDimension = 2;
193
194 // parse out 2 or 3 tuple.
195 dfX = atof( pszCoordString );
196 while( *pszCoordString != '\0'
197 && *pszCoordString != ','
198 && !isspace(*pszCoordString) )
199 pszCoordString++;
200
201 if( *pszCoordString == '\0' || isspace(*pszCoordString) )
202 {
203 CPLError( CE_Failure, CPLE_AppDefined,
204 "Corrupt <coordinates> value." );
205 return FALSE;
206 }
207
208 pszCoordString++;
209 dfY = atof( pszCoordString );
210 while( *pszCoordString != '\0'
211 && *pszCoordString != ','
212 && !isspace(*pszCoordString) )
213 pszCoordString++;
214
215 if( *pszCoordString == ',' )
216 {
217 pszCoordString++;
218 dfZ = atof( pszCoordString );
219 nDimension = 3;
220 while( *pszCoordString != '\0'
221 && *pszCoordString != ','
222 && !isspace(*pszCoordString) )
223 pszCoordString++;
224 }
225
226 while( isspace(*pszCoordString) )
227 pszCoordString++;
228
229 if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) )
230 return FALSE;
231
232 iCoord++;
233 }
234
235 return iCoord > 0;
236 }
237
238 /* -------------------------------------------------------------------- */
239 /* Is this a "pos"? GML 3 construct. */
240 /* Parse if it exist a series of pos elements (this would allow */
241 /* the correct parsing of gml3.1.1 geomtries such as linestring */
242 /* defined with pos elements. */
243 /* -------------------------------------------------------------------- */
244 CPLXMLNode *psPos;
245
246 for( psPos = psGeomNode->psChild;
247 psPos != NULL;
248 psPos = psPos->psNext )
249 {
250 if( psPos->eType != CXT_Element
251 || !EQUAL(BareGMLElement(psPos->pszValue),"pos") )
252 continue;
253
254 char **papszTokens = CSLTokenizeStringComplex(
255 GetElementText( psPos ), " ,", FALSE, FALSE );
256 int bSuccess = FALSE;
257
258 if( CSLCount( papszTokens ) > 2 )
259 {
260 bSuccess = AddPoint( poGeometry,
261 atof(papszTokens[0]),
262 atof(papszTokens[1]),
263 atof(papszTokens[2]), 3 );
264 }
265 else if( CSLCount( papszTokens ) > 1 )
266 {
267 bSuccess = AddPoint( poGeometry,
268 atof(papszTokens[0]),
269 atof(papszTokens[1]),
270 0.0, 2 );
271 }
272 else
273 {
274 CPLError( CE_Failure, CPLE_AppDefined,
275 "Did not get 2+ values in <gml:pos>%s</gml:pos> tuple.",
276 GetElementText( psPos ) );
277 }
278
279 CSLDestroy( papszTokens );
280
281 return bSuccess;
282 }
283
284
285 /* -------------------------------------------------------------------- */
286 /* Is this a "posList"? GML 3 construct (SF profile). */
287 /* -------------------------------------------------------------------- */
288 CPLXMLNode *psPosList = FindBareXMLChild( psGeomNode, "posList" );
289
290 if( psPosList != NULL )
291 {
292 char **papszTokens = CSLTokenizeStringComplex(
293 GetElementText( psPosList ), " ,", FALSE, FALSE );
294 int bSuccess = FALSE;
295 int i=0, nCount=0;
296
297 /*assuming that it is a 2 dimension with x y values*/
298 /*we could also check to see if there is a count attribute and an srsDimension.
299 These attributes are only availabe for gml3.1.1 but not
300 available for gml3.1 SF*/
301
302 nCount = CSLCount( papszTokens );
303
304 if (nCount < 2 || fmod((double)nCount, 2.0) != 0)
305 {
306
307 CPLError( CE_Failure, CPLE_AppDefined,
308 "Did not get at least two values or invalid number of \n"
309 "set of coordinates <gml:posList>%s</gml:posList>",
310 GetElementText( psPosList ) );
311 }
312 else
313 {
314 i=0;
315 while (i<nCount)
316 {
317 bSuccess = AddPoint( poGeometry,
318 atof(papszTokens[i]),
319 atof(papszTokens[i+1]),
320 0.0, 2 );
321 i+=2;
322 }
323 }
324 CSLDestroy( papszTokens );
325
326 return bSuccess;
327 }
328
329
330 /* -------------------------------------------------------------------- */
331 /* Handle form with a list of <coord> items each with an <X>, */
332 /* and <Y> element. */
333 /* -------------------------------------------------------------------- */
334 CPLXMLNode *psCoordNode;
335
336 for( psCoordNode = psGeomNode->psChild;
337 psCoordNode != NULL;
338 psCoordNode = psCoordNode->psNext )
339 {
340 if( psCoordNode->eType != CXT_Element
341 || !EQUAL(BareGMLElement(psCoordNode->pszValue),"coord") )
342 continue;
343
344 CPLXMLNode *psXNode, *psYNode, *psZNode;
345 double dfX, dfY, dfZ = 0.0;
346 int nDimension = 2;
347
348 psXNode = FindBareXMLChild( psCoordNode, "X" );
349 psYNode = FindBareXMLChild( psCoordNode, "Y" );
350 psZNode = FindBareXMLChild( psCoordNode, "Z" );
351
352 if( psXNode == NULL || psYNode == NULL
353 || GetElementText(psXNode) == NULL
354 || GetElementText(psYNode) == NULL
355 || (psZNode != NULL && GetElementText(psZNode) == NULL) )
356 {
357 CPLError( CE_Failure, CPLE_AppDefined,
358 "Corrupt <coord> element, missing <X> or <Y> element?" );
359 return FALSE;
360 }
361
362 dfX = atof( GetElementText(psXNode) );
363 dfY = atof( GetElementText(psYNode) );
364
365 if( psZNode != NULL && GetElementText(psZNode) != NULL )
366 {
367 dfZ = atof( GetElementText(psZNode) );
368 nDimension = 3;
369 }
370
371 if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) )
372 return FALSE;
373
374 iCoord++;
375 }
376
377 return iCoord > 0.0;
378 }
379
380 /************************************************************************/
381 /* GML2OGRGeometry_XMLNode() */
382 /* */
383 /* Translates the passed XMLnode and it's children into an */
384 /* OGRGeometry. This is used recursively for geometry */
385 /* collections. */
386 /************************************************************************/
387
GML2OGRGeometry_XMLNode(CPLXMLNode * psNode)388 static OGRGeometry *GML2OGRGeometry_XMLNode( CPLXMLNode *psNode )
389
390 {
391 const char *pszBaseGeometry = BareGMLElement( psNode->pszValue );
392
393 /* -------------------------------------------------------------------- */
394 /* Polygon */
395 /* -------------------------------------------------------------------- */
396 if( EQUAL(pszBaseGeometry,"Polygon") )
397 {
398 CPLXMLNode *psChild;
399 OGRPolygon *poPolygon = new OGRPolygon();
400 OGRLinearRing *poRing;
401
402 // Find outer ring.
403 psChild = FindBareXMLChild( psNode, "outerBoundaryIs" );
404 if (psChild == NULL)
405 psChild = FindBareXMLChild( psNode, "exterior");
406
407 if( psChild == NULL || psChild->psChild == NULL )
408 {
409 CPLError( CE_Failure, CPLE_AppDefined,
410 "Missing outerBoundaryIs property on Polygon." );
411 delete poPolygon;
412 return NULL;
413 }
414
415 // Translate outer ring and add to polygon.
416 poRing = (OGRLinearRing *)
417 GML2OGRGeometry_XMLNode( psChild->psChild );
418 if( poRing == NULL )
419 {
420 delete poPolygon;
421 return NULL;
422 }
423
424 if( !EQUAL(poRing->getGeometryName(),"LINEARRING") )
425 {
426 CPLError( CE_Failure, CPLE_AppDefined,
427 "Got %.500s geometry as outerBoundaryIs instead of LINEARRING.",
428 poRing->getGeometryName() );
429 delete poPolygon;
430 delete poRing;
431 return NULL;
432 }
433
434 poPolygon->addRingDirectly( poRing );
435
436 // Find all inner rings
437 for( psChild = psNode->psChild;
438 psChild != NULL;
439 psChild = psChild->psNext )
440 {
441 if( psChild->eType == CXT_Element
442 && (EQUAL(BareGMLElement(psChild->pszValue),"innerBoundaryIs") ||
443 EQUAL(BareGMLElement(psChild->pszValue),"interior")))
444 {
445 poRing = (OGRLinearRing *)
446 GML2OGRGeometry_XMLNode( psChild->psChild );
447 if( !EQUAL(poRing->getGeometryName(),"LINEARRING") )
448 {
449 CPLError( CE_Failure, CPLE_AppDefined,
450 "Got %.500s geometry as innerBoundaryIs instead of LINEARRING.",
451 poRing->getGeometryName() );
452 delete poPolygon;
453 delete poRing;
454 return NULL;
455 }
456
457 poPolygon->addRingDirectly( poRing );
458 }
459 }
460
461 return poPolygon;
462 }
463
464 /* -------------------------------------------------------------------- */
465 /* LinearRing */
466 /* -------------------------------------------------------------------- */
467 if( EQUAL(pszBaseGeometry,"LinearRing") )
468 {
469 OGRLinearRing *poLinearRing = new OGRLinearRing();
470
471 if( !ParseGMLCoordinates( psNode, poLinearRing ) )
472 {
473 delete poLinearRing;
474 return NULL;
475 }
476
477 return poLinearRing;
478 }
479
480 /* -------------------------------------------------------------------- */
481 /* LineString */
482 /* -------------------------------------------------------------------- */
483 if( EQUAL(pszBaseGeometry,"LineString") )
484 {
485 OGRLineString *poLine = new OGRLineString();
486
487 if( !ParseGMLCoordinates( psNode, poLine ) )
488 {
489 delete poLine;
490 return NULL;
491 }
492
493 return poLine;
494 }
495
496 /* -------------------------------------------------------------------- */
497 /* PointType */
498 /* -------------------------------------------------------------------- */
499 if( EQUAL(pszBaseGeometry,"PointType")
500 || EQUAL(pszBaseGeometry,"Point") )
501 {
502 OGRPoint *poPoint = new OGRPoint();
503
504 if( !ParseGMLCoordinates( psNode, poPoint ) )
505 {
506 delete poPoint;
507 return NULL;
508 }
509
510 return poPoint;
511 }
512
513 /* -------------------------------------------------------------------- */
514 /* Box */
515 /* -------------------------------------------------------------------- */
516 if( EQUAL(pszBaseGeometry,"BoxType") || EQUAL(pszBaseGeometry,"Box") )
517 {
518 OGRLineString oPoints;
519
520 if( !ParseGMLCoordinates( psNode, &oPoints ) )
521 return NULL;
522
523 if( oPoints.getNumPoints() < 2 )
524 return NULL;
525
526 OGRLinearRing *poBoxRing = new OGRLinearRing();
527 OGRPolygon *poBoxPoly = new OGRPolygon();
528
529 poBoxRing->setNumPoints( 5 );
530 poBoxRing->setPoint(
531 0, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) );
532 poBoxRing->setPoint(
533 1, oPoints.getX(1), oPoints.getY(0), oPoints.getZ(0) );
534 poBoxRing->setPoint(
535 2, oPoints.getX(1), oPoints.getY(1), oPoints.getZ(1) );
536 poBoxRing->setPoint(
537 3, oPoints.getX(0), oPoints.getY(1), oPoints.getZ(0) );
538 poBoxRing->setPoint(
539 4, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) );
540
541 poBoxPoly->addRingDirectly( poBoxRing );
542
543 return poBoxPoly;
544 }
545
546 /* -------------------------------------------------------------------- */
547 /* MultiPolygon */
548 /* -------------------------------------------------------------------- */
549 if( EQUAL(pszBaseGeometry,"MultiPolygon") )
550 {
551 CPLXMLNode *psChild;
552 OGRMultiPolygon *poMPoly = new OGRMultiPolygon();
553
554 // Find all inner rings
555 for( psChild = psNode->psChild;
556 psChild != NULL;
557 psChild = psChild->psNext )
558 {
559 if( psChild->eType == CXT_Element
560 && EQUAL(BareGMLElement(psChild->pszValue),"polygonMember") )
561 {
562 OGRPolygon *poPolygon;
563
564 poPolygon = (OGRPolygon *)
565 GML2OGRGeometry_XMLNode( psChild->psChild );
566
567 if( poPolygon == NULL )
568 {
569 delete poMPoly;
570 return NULL;
571 }
572
573 if( !EQUAL(poPolygon->getGeometryName(),"POLYGON") )
574 {
575 CPLError( CE_Failure, CPLE_AppDefined,
576 "Got %.500s geometry as polygonMember instead of MULTIPOLYGON.",
577 poPolygon->getGeometryName() );
578 delete poPolygon;
579 delete poMPoly;
580 return NULL;
581 }
582
583 poMPoly->addGeometryDirectly( poPolygon );
584 }
585 }
586
587 return poMPoly;
588 }
589
590 /* -------------------------------------------------------------------- */
591 /* MultiPoint */
592 /* -------------------------------------------------------------------- */
593 if( EQUAL(pszBaseGeometry,"MultiPoint") )
594 {
595 CPLXMLNode *psChild;
596 OGRMultiPoint *poMP = new OGRMultiPoint();
597
598 // collect points.
599 for( psChild = psNode->psChild;
600 psChild != NULL;
601 psChild = psChild->psNext )
602 {
603 if( psChild->eType == CXT_Element
604 && EQUAL(BareGMLElement(psChild->pszValue),"pointMember") )
605 {
606 OGRPoint *poPoint;
607
608 poPoint = (OGRPoint *)
609 GML2OGRGeometry_XMLNode( psChild->psChild );
610 if( poPoint == NULL
611 || wkbFlatten(poPoint->getGeometryType()) != wkbPoint )
612 {
613 CPLError( CE_Failure, CPLE_AppDefined,
614 "Got %.500s geometry as pointMember instead of MULTIPOINT",
615 poPoint ? poPoint->getGeometryName() : "NULL" );
616 delete poPoint;
617 delete poMP;
618 return NULL;
619 }
620
621 poMP->addGeometryDirectly( poPoint );
622 }
623 }
624
625 return poMP;
626 }
627
628 /* -------------------------------------------------------------------- */
629 /* MultiLineString */
630 /* -------------------------------------------------------------------- */
631 if( EQUAL(pszBaseGeometry,"MultiLineString") )
632 {
633 CPLXMLNode *psChild;
634 OGRMultiLineString *poMP = new OGRMultiLineString();
635
636 // collect lines
637 for( psChild = psNode->psChild;
638 psChild != NULL;
639 psChild = psChild->psNext )
640 {
641 if( psChild->eType == CXT_Element
642 && EQUAL(BareGMLElement(psChild->pszValue),"lineStringMember") )
643 {
644 OGRGeometry *poGeom;
645
646 poGeom = GML2OGRGeometry_XMLNode( psChild->psChild );
647 if( poGeom == NULL
648 || wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
649 {
650 CPLError( CE_Failure, CPLE_AppDefined,
651 "Got %.500s geometry as Member instead of LINESTRING.",
652 poGeom ? poGeom->getGeometryName() : "NULL" );
653 delete poGeom;
654 delete poMP;
655 return NULL;
656 }
657
658 poMP->addGeometryDirectly( poGeom );
659 }
660 }
661
662 return poMP;
663 }
664
665 /* -------------------------------------------------------------------- */
666 /* GeometryCollection */
667 /* -------------------------------------------------------------------- */
668 if( EQUAL(pszBaseGeometry,"GeometryCollection") )
669 {
670 CPLXMLNode *psChild;
671 OGRGeometryCollection *poGC = new OGRGeometryCollection();
672
673 // collect geoms
674 for( psChild = psNode->psChild;
675 psChild != NULL;
676 psChild = psChild->psNext )
677 {
678 if( psChild->eType == CXT_Element
679 && EQUAL(BareGMLElement(psChild->pszValue),"geometryMember") )
680 {
681 OGRGeometry *poGeom;
682
683 poGeom = GML2OGRGeometry_XMLNode( psChild->psChild );
684 if( poGeom == NULL )
685 {
686 CPLError( CE_Failure, CPLE_AppDefined,
687 "Failed to get geometry in geometryMember" );
688 delete poGeom;
689 delete poGC;
690 return NULL;
691 }
692
693 poGC->addGeometryDirectly( poGeom );
694 }
695 }
696
697 return poGC;
698 }
699
700 CPLError( CE_Failure, CPLE_AppDefined,
701 "Unrecognised geometry type <%.500s>.",
702 pszBaseGeometry );
703
704 return NULL;
705 }
706
707 /************************************************************************/
708 /* OGR_G_CreateFromGMLTree() */
709 /************************************************************************/
710
OGR_G_CreateFromGMLTree(const CPLXMLNode * psTree)711 OGRGeometryH OGR_G_CreateFromGMLTree( const CPLXMLNode *psTree )
712
713 {
714 return (OGRGeometryH) GML2OGRGeometry_XMLNode( (CPLXMLNode *) psTree );
715 }
716
717 /************************************************************************/
718 /* OGR_G_CreateFromGML() */
719 /************************************************************************/
720
OGR_G_CreateFromGML(const char * pszGML)721 OGRGeometryH OGR_G_CreateFromGML( const char *pszGML )
722
723 {
724 if( pszGML == NULL || strlen(pszGML) == 0 )
725 {
726 CPLError( CE_Failure, CPLE_AppDefined,
727 "GML Geometry is empty in GML2OGRGeometry()." );
728 return NULL;
729 }
730
731 /* -------------------------------------------------------------------- */
732 /* Try to parse the XML snippet using the MiniXML API. If this */
733 /* fails, we assume the minixml api has already posted a CPL */
734 /* error, and just return NULL. */
735 /* -------------------------------------------------------------------- */
736 CPLXMLNode *psGML = CPLParseXMLString( pszGML );
737
738 if( psGML == NULL )
739 return NULL;
740
741 /* -------------------------------------------------------------------- */
742 /* Convert geometry recursively. */
743 /* -------------------------------------------------------------------- */
744 OGRGeometry *poGeometry;
745
746 poGeometry = GML2OGRGeometry_XMLNode( psGML );
747
748 CPLDestroyXMLNode( psGML );
749
750 return (OGRGeometryH) poGeometry;
751 }
752