1 : /******************************************************************************
2 : * $Id: gml2ogrgeometry.cpp 25335 2012-12-20 20:27:03Z rouault $
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 "cpl_minixml.h"
42 : #include "ogr_geometry.h"
43 : #include "ogr_api.h"
44 : #include "cpl_error.h"
45 : #include "cpl_string.h"
46 : #include <ctype.h>
47 : #include "ogr_p.h"
48 :
49 : #ifndef PI
50 : #define PI 3.14159265358979323846
51 : #endif
52 :
53 :
54 : /************************************************************************/
55 : /* GMLGetCoordTokenPos() */
56 : /************************************************************************/
57 :
58 564740 : static const char* GMLGetCoordTokenPos(const char* pszStr,
59 : const char** ppszNextToken)
60 : {
61 : char ch;
62 279772 : while(TRUE)
63 : {
64 564740 : ch = *pszStr;
65 564740 : if (ch == '\0')
66 : {
67 23 : *ppszNextToken = NULL;
68 23 : return NULL;
69 : }
70 564717 : else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ','))
71 : break;
72 279772 : pszStr ++;
73 : }
74 :
75 284945 : const char* pszToken = pszStr;
76 3507935 : while((ch = *pszStr) != '\0')
77 : {
78 3217636 : if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',')
79 : {
80 279591 : *ppszNextToken = pszStr;
81 279591 : return pszToken;
82 : }
83 2938045 : pszStr ++;
84 : }
85 5354 : *ppszNextToken = NULL;
86 5354 : return pszToken;
87 : }
88 :
89 : /************************************************************************/
90 : /* BareGMLElement() */
91 : /* */
92 : /* Returns the passed string with any namespace prefix */
93 : /* stripped off. */
94 : /************************************************************************/
95 :
96 54285 : static const char *BareGMLElement( const char *pszInput )
97 :
98 : {
99 : const char *pszReturn;
100 :
101 54285 : pszReturn = strchr( pszInput, ':' );
102 54285 : if( pszReturn == NULL )
103 53170 : pszReturn = pszInput;
104 : else
105 1115 : pszReturn++;
106 :
107 54285 : return pszReturn;
108 : }
109 :
110 : /************************************************************************/
111 : /* FindBareXMLChild() */
112 : /* */
113 : /* Find a child node with the indicated "bare" name, that is */
114 : /* after any namespace qualifiers have been stripped off. */
115 : /************************************************************************/
116 :
117 19093 : static const CPLXMLNode *FindBareXMLChild( const CPLXMLNode *psParent,
118 : const char *pszBareName )
119 :
120 : {
121 19093 : const CPLXMLNode *psCandidate = psParent->psChild;
122 :
123 56990 : while( psCandidate != NULL )
124 : {
125 30731 : if( psCandidate->eType == CXT_Element
126 : && EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName) )
127 11927 : return psCandidate;
128 :
129 18804 : psCandidate = psCandidate->psNext;
130 : }
131 :
132 7166 : return NULL;
133 : }
134 :
135 : /************************************************************************/
136 : /* GetElementText() */
137 : /************************************************************************/
138 :
139 5601 : static const char *GetElementText( const CPLXMLNode *psElement )
140 :
141 : {
142 5601 : if( psElement == NULL )
143 0 : return NULL;
144 :
145 5601 : const CPLXMLNode *psChild = psElement->psChild;
146 :
147 12009 : while( psChild != NULL )
148 : {
149 6402 : if( psChild->eType == CXT_Text )
150 5595 : return psChild->pszValue;
151 :
152 807 : psChild = psChild->psNext;
153 : }
154 :
155 6 : return NULL;
156 : }
157 :
158 : /************************************************************************/
159 : /* GetChildElement() */
160 : /************************************************************************/
161 :
162 5836 : static const CPLXMLNode *GetChildElement( const CPLXMLNode *psElement )
163 :
164 : {
165 5836 : if( psElement == NULL )
166 9 : return NULL;
167 :
168 5827 : const CPLXMLNode *psChild = psElement->psChild;
169 :
170 11686 : while( psChild != NULL )
171 : {
172 5849 : if( psChild->eType == CXT_Element )
173 5817 : return psChild;
174 :
175 32 : psChild = psChild->psNext;
176 : }
177 :
178 10 : return NULL;
179 : }
180 :
181 : /************************************************************************/
182 : /* GetElementOrientation() */
183 : /* Returns true for positive orientation. */
184 : /************************************************************************/
185 :
186 712 : int GetElementOrientation( const CPLXMLNode *psElement )
187 : {
188 712 : if( psElement == NULL )
189 0 : return TRUE;
190 :
191 712 : const CPLXMLNode *psChild = psElement->psChild;
192 :
193 1908 : while( psChild != NULL )
194 : {
195 712 : if( psChild->eType == CXT_Attribute &&
196 : EQUAL(psChild->pszValue,"orientation") )
197 228 : return EQUAL(psChild->psChild->pszValue,"+");
198 :
199 484 : psChild = psChild->psNext;
200 : }
201 :
202 484 : return TRUE;
203 : }
204 :
205 : /************************************************************************/
206 : /* AddPoint() */
207 : /* */
208 : /* Add a point to the passed geometry. */
209 : /************************************************************************/
210 :
211 142975 : static int AddPoint( OGRGeometry *poGeometry,
212 : double dfX, double dfY, double dfZ, int nDimension )
213 :
214 : {
215 142975 : OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
216 142975 : if( eType == wkbPoint )
217 : {
218 573 : OGRPoint *poPoint = (OGRPoint *) poGeometry;
219 :
220 573 : if( !poPoint->IsEmpty() )
221 : {
222 : CPLError( CE_Failure, CPLE_AppDefined,
223 2 : "More than one coordinate for <Point> element.");
224 2 : return FALSE;
225 : }
226 :
227 571 : poPoint->setX( dfX );
228 571 : poPoint->setY( dfY );
229 571 : if( nDimension == 3 )
230 12 : poPoint->setZ( dfZ );
231 :
232 571 : return TRUE;
233 : }
234 :
235 142402 : else if( eType == wkbLineString )
236 : {
237 142402 : if( nDimension == 3 )
238 57 : ((OGRLineString *) poGeometry)->addPoint( dfX, dfY, dfZ );
239 : else
240 142345 : ((OGRLineString *) poGeometry)->addPoint( dfX, dfY );
241 :
242 142402 : return TRUE;
243 : }
244 :
245 : else
246 : {
247 0 : CPLAssert( FALSE );
248 0 : return FALSE;
249 : }
250 : }
251 :
252 : /************************************************************************/
253 : /* ParseGMLCoordinates() */
254 : /************************************************************************/
255 :
256 5453 : static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeometry )
257 :
258 : {
259 5453 : const CPLXMLNode *psCoordinates = FindBareXMLChild( psGeomNode, "coordinates" );
260 5453 : int iCoord = 0;
261 :
262 : /* -------------------------------------------------------------------- */
263 : /* Handle <coordinates> case. */
264 : /* -------------------------------------------------------------------- */
265 5453 : if( psCoordinates != NULL )
266 : {
267 211 : const char *pszCoordString = GetElementText( psCoordinates );
268 211 : char chCS = ',';
269 :
270 211 : if( pszCoordString == NULL )
271 : {
272 1 : poGeometry->empty();
273 1 : return TRUE;
274 : }
275 :
276 943 : while( *pszCoordString != '\0' )
277 : {
278 525 : double dfX, dfY, dfZ = 0.0;
279 525 : int nDimension = 2;
280 :
281 : // parse out 2 or 3 tuple.
282 525 : dfX = OGRFastAtof( pszCoordString );
283 5892 : while( *pszCoordString != '\0'
284 : && *pszCoordString != ','
285 : && !isspace((unsigned char)*pszCoordString) )
286 4842 : pszCoordString++;
287 :
288 525 : if( *pszCoordString == '\0' )
289 : {
290 : CPLError( CE_Failure, CPLE_AppDefined,
291 1 : "Corrupt <coordinates> value." );
292 1 : return FALSE;
293 : }
294 524 : else if( chCS == ',' && isspace((unsigned char)*pszCoordString) )
295 : {
296 : /* In theory, the coordinates inside a coordinate tuple should be */
297 : /* separated by a comma. However it has been found in the wild */
298 : /* that the coordinates are in rare cases separated by a space, and the tuples by a comma */
299 : /* See https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample */
300 : /* or http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW */
301 2 : chCS = ' ';
302 : }
303 :
304 524 : pszCoordString++;
305 524 : dfY = OGRFastAtof( pszCoordString );
306 5967 : while( *pszCoordString != '\0'
307 : && *pszCoordString != ','
308 : && !isspace((unsigned char)*pszCoordString) )
309 4919 : pszCoordString++;
310 :
311 524 : if( *pszCoordString == chCS )
312 : {
313 25 : pszCoordString++;
314 25 : dfZ = OGRFastAtof( pszCoordString );
315 25 : nDimension = 3;
316 75 : while( *pszCoordString != '\0'
317 : && *pszCoordString != ','
318 : && !isspace((unsigned char)*pszCoordString) )
319 25 : pszCoordString++;
320 : }
321 :
322 524 : if ( chCS == ' ' && *pszCoordString == ',' )
323 : {
324 0 : pszCoordString++;
325 : }
326 :
327 1392 : while( isspace((unsigned char)*pszCoordString) )
328 344 : pszCoordString++;
329 :
330 524 : if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) )
331 1 : return FALSE;
332 :
333 523 : iCoord++;
334 : }
335 :
336 208 : return iCoord > 0;
337 : }
338 :
339 : /* -------------------------------------------------------------------- */
340 : /* Is this a "pos"? GML 3 construct. */
341 : /* Parse if it exist a series of pos elements (this would allow */
342 : /* the correct parsing of gml3.1.1 geomtries such as linestring */
343 : /* defined with pos elements. */
344 : /* -------------------------------------------------------------------- */
345 : const CPLXMLNode *psPos;
346 :
347 5242 : int bHasFoundPosElement = FALSE;
348 11424 : for( psPos = psGeomNode->psChild;
349 : psPos != NULL;
350 : psPos = psPos->psNext )
351 : {
352 6185 : if( psPos->eType != CXT_Element )
353 791 : continue;
354 :
355 5394 : const char* pszSubElement = BareGMLElement(psPos->pszValue);
356 :
357 5394 : if( EQUAL(pszSubElement, "pointProperty") )
358 : {
359 : const CPLXMLNode *psPointPropertyIter;
360 2 : for( psPointPropertyIter = psPos->psChild;
361 : psPointPropertyIter != NULL;
362 : psPointPropertyIter = psPointPropertyIter->psNext )
363 : {
364 1 : if( psPointPropertyIter->eType != CXT_Element )
365 0 : continue;
366 :
367 1 : if (EQUAL(BareGMLElement(psPointPropertyIter->pszValue),"Point") )
368 : {
369 1 : OGRPoint oPoint;
370 1 : if( ParseGMLCoordinates( psPointPropertyIter, &oPoint ) )
371 : {
372 : int bSuccess = AddPoint( poGeometry, oPoint.getX(),
373 : oPoint.getY(), oPoint.getZ(),
374 1 : oPoint.getCoordinateDimension() );
375 1 : if (bSuccess)
376 1 : bHasFoundPosElement = TRUE;
377 : else
378 0 : return FALSE;
379 0 : }
380 : }
381 : }
382 1 : continue;
383 : }
384 :
385 5393 : if( !EQUAL(pszSubElement,"pos") )
386 4814 : continue;
387 :
388 579 : const char* pszPos = GetElementText( psPos );
389 579 : if (pszPos == NULL)
390 : {
391 1 : poGeometry->empty();
392 1 : return TRUE;
393 : }
394 :
395 578 : const char* pszCur = pszPos;
396 : const char* pszX = (pszCur != NULL) ?
397 578 : GMLGetCoordTokenPos(pszCur, &pszCur) : NULL;
398 : const char* pszY = (pszCur != NULL) ?
399 578 : GMLGetCoordTokenPos(pszCur, &pszCur) : NULL;
400 : const char* pszZ = (pszCur != NULL) ?
401 578 : GMLGetCoordTokenPos(pszCur, &pszCur) : NULL;
402 :
403 578 : if (pszY == NULL)
404 : {
405 : CPLError( CE_Failure, CPLE_AppDefined,
406 : "Did not get 2+ values in <gml:pos>%s</gml:pos> tuple.",
407 1 : pszPos ? pszPos : "" );
408 1 : return FALSE;
409 : }
410 :
411 577 : double dfX = OGRFastAtof(pszX);
412 577 : double dfY = OGRFastAtof(pszY);
413 577 : double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0;
414 577 : int bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, (pszZ != NULL) ? 3 : 2 );
415 :
416 577 : if (bSuccess)
417 576 : bHasFoundPosElement = TRUE;
418 : else
419 1 : return FALSE;
420 : }
421 :
422 5239 : if (bHasFoundPosElement)
423 416 : return TRUE;
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* Is this a "posList"? GML 3 construct (SF profile). */
427 : /* -------------------------------------------------------------------- */
428 4823 : const CPLXMLNode *psPosList = FindBareXMLChild( psGeomNode, "posList" );
429 :
430 4823 : if( psPosList != NULL )
431 : {
432 4803 : int bSuccess = FALSE;
433 4803 : int nDimension = 2;
434 :
435 : /* Try to detect the presence of an srsDimension attribute */
436 : /* This attribute is only availabe for gml3.1.1 but not */
437 : /* available for gml3.1 SF*/
438 4803 : const char* pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psPosList, "srsDimension", NULL);
439 : /* If not found at the posList level, try on the enclosing element */
440 4803 : if (pszSRSDimension == NULL)
441 4097 : pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psGeomNode, "srsDimension", NULL);
442 4803 : if (pszSRSDimension != NULL)
443 711 : nDimension = atoi(pszSRSDimension);
444 :
445 4803 : if (nDimension != 2 && nDimension != 3)
446 : {
447 : CPLError( CE_Failure, CPLE_AppDefined,
448 1 : "srsDimension = %d not supported", nDimension);
449 1 : return FALSE;
450 : }
451 :
452 4802 : const char* pszPosList = GetElementText( psPosList );
453 4802 : if (pszPosList == NULL)
454 : {
455 3 : poGeometry->empty();
456 3 : return TRUE;
457 : }
458 :
459 4799 : const char* pszCur = pszPosList;
460 137098 : while (TRUE)
461 : {
462 141897 : const char* pszX = GMLGetCoordTokenPos(pszCur, &pszCur);
463 141897 : if (pszX == NULL && bSuccess)
464 23 : break;
465 : const char* pszY = (pszCur != NULL) ?
466 141874 : GMLGetCoordTokenPos(pszCur, &pszCur) : NULL;
467 : const char* pszZ = (nDimension == 3 && pszCur != NULL) ?
468 141874 : GMLGetCoordTokenPos(pszCur, &pszCur) : NULL;
469 :
470 141874 : if (pszY == NULL || (nDimension == 3 && pszZ == NULL))
471 : {
472 : CPLError( CE_Failure, CPLE_AppDefined,
473 : "Did not get at least %d values or invalid number of \n"
474 : "set of coordinates <gml:posList>%s</gml:posList>",
475 3 : nDimension, pszPosList ? pszPosList : "");
476 3 : return FALSE;
477 : }
478 :
479 141871 : double dfX = OGRFastAtof(pszX);
480 141871 : double dfY = OGRFastAtof(pszY);
481 141871 : double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0;
482 141871 : bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, nDimension );
483 :
484 141871 : if (bSuccess == FALSE || pszCur == NULL)
485 4773 : break;
486 : }
487 :
488 4796 : return bSuccess;
489 : }
490 :
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Handle form with a list of <coord> items each with an <X>, */
494 : /* and <Y> element. */
495 : /* -------------------------------------------------------------------- */
496 : const CPLXMLNode *psCoordNode;
497 :
498 30 : for( psCoordNode = psGeomNode->psChild;
499 : psCoordNode != NULL;
500 : psCoordNode = psCoordNode->psNext )
501 : {
502 13 : if( psCoordNode->eType != CXT_Element
503 : || !EQUAL(BareGMLElement(psCoordNode->pszValue),"coord") )
504 8 : continue;
505 :
506 : const CPLXMLNode *psXNode, *psYNode, *psZNode;
507 5 : double dfX, dfY, dfZ = 0.0;
508 5 : int nDimension = 2;
509 :
510 5 : psXNode = FindBareXMLChild( psCoordNode, "X" );
511 5 : psYNode = FindBareXMLChild( psCoordNode, "Y" );
512 5 : psZNode = FindBareXMLChild( psCoordNode, "Z" );
513 :
514 5 : if( psXNode == NULL || psYNode == NULL
515 : || GetElementText(psXNode) == NULL
516 : || GetElementText(psYNode) == NULL
517 : || (psZNode != NULL && GetElementText(psZNode) == NULL) )
518 : {
519 : CPLError( CE_Failure, CPLE_AppDefined,
520 3 : "Corrupt <coord> element, missing <X> or <Y> element?" );
521 3 : return FALSE;
522 : }
523 :
524 2 : dfX = OGRFastAtof( GetElementText(psXNode) );
525 2 : dfY = OGRFastAtof( GetElementText(psYNode) );
526 :
527 2 : if( psZNode != NULL && GetElementText(psZNode) != NULL )
528 : {
529 0 : dfZ = OGRFastAtof( GetElementText(psZNode) );
530 0 : nDimension = 3;
531 : }
532 :
533 2 : if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) )
534 0 : return FALSE;
535 :
536 2 : iCoord++;
537 : }
538 :
539 17 : return iCoord > 0.0;
540 : }
541 :
542 : #ifdef HAVE_GEOS
543 : /************************************************************************/
544 : /* GML2FaceExtRing() */
545 : /* */
546 : /* Identifies the "good" Polygon whithin the collection returned */
547 : /* by GEOSPolygonize() */
548 : /* short rationale: GEOSPolygonize() will possibily return a */
549 : /* collection of many Polygons; only one is the "good" one, */
550 : /* (including both exterior- and interior-rings) */
551 : /* any other simply represents a single "hole", and should be */
552 : /* consequently ignored at all. */
553 : /************************************************************************/
554 :
555 66 : static OGRPolygon *GML2FaceExtRing( OGRGeometry *poGeom )
556 : {
557 66 : OGRPolygon *poPolygon = NULL;
558 66 : int bError = FALSE;
559 66 : OGRGeometryCollection *poColl = (OGRGeometryCollection *)poGeom;
560 66 : int iCount = poColl->getNumGeometries();
561 66 : int iExterior = 0;
562 66 : int iInterior = 0;
563 :
564 146 : for( int ig = 0; ig < iCount; ig++)
565 : {
566 : /* a collection of Polygons is expected to be found */
567 80 : OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig);
568 80 : if( poChild == NULL)
569 : {
570 0 : bError = TRUE;
571 0 : continue;
572 : }
573 80 : if( wkbFlatten( poChild->getGeometryType()) == wkbPolygon )
574 : {
575 80 : OGRPolygon *poPg = (OGRPolygon *)poChild;
576 80 : if( poPg->getNumInteriorRings() > 0 )
577 10 : iExterior++;
578 : else
579 70 : iInterior++;
580 : }
581 : else
582 0 : bError = TRUE;
583 : }
584 :
585 66 : if( bError == FALSE && iCount > 0 )
586 : {
587 122 : if( iCount == 1 && iExterior == 0 && iInterior == 1)
588 : {
589 : /* there is a single Polygon within the collection */
590 56 : OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(0 );
591 56 : poPolygon = (OGRPolygon *)poPg->clone();
592 : }
593 : else
594 : {
595 10 : if( iExterior == 1 && iInterior == iCount - 1 )
596 : {
597 : /* searching the unique Polygon containing holes */
598 34 : for ( int ig = 0; ig < iCount; ig++)
599 : {
600 24 : OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(ig);
601 24 : if( poPg->getNumInteriorRings() > 0 )
602 10 : poPolygon = (OGRPolygon *)poPg->clone();
603 : }
604 : }
605 : }
606 : }
607 :
608 66 : return poPolygon;
609 : }
610 : #endif
611 :
612 : /************************************************************************/
613 : /* GML2OGRGeometry_XMLNode() */
614 : /* */
615 : /* Translates the passed XMLnode and it's children into an */
616 : /* OGRGeometry. This is used recursively for geometry */
617 : /* collections. */
618 : /************************************************************************/
619 :
620 15691 : OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode,
621 : int bGetSecondaryGeometryOption,
622 : int nRecLevel,
623 : int bIgnoreGSG,
624 : int bOrientation,
625 : int bFaceHoleNegative )
626 :
627 : {
628 15691 : const char *pszBaseGeometry = BareGMLElement( psNode->pszValue );
629 15691 : if (bGetSecondaryGeometryOption < 0)
630 518 : bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
631 15691 : int bGetSecondaryGeometry = bIgnoreGSG ? FALSE : bGetSecondaryGeometryOption;
632 :
633 : /* Arbitrary value, but certainly large enough for reasonable usages ! */
634 15691 : if( nRecLevel == 32 )
635 : {
636 : CPLError( CE_Failure, CPLE_AppDefined,
637 : "Too many recursion levels (%d) while parsing GML geometry.",
638 1 : nRecLevel );
639 1 : return NULL;
640 : }
641 :
642 15690 : if( bGetSecondaryGeometry )
643 0 : if( !( EQUAL(pszBaseGeometry,"directedEdge") ||
644 : EQUAL(pszBaseGeometry,"TopoCurve") ) )
645 0 : return NULL;
646 :
647 : /* -------------------------------------------------------------------- */
648 : /* Polygon / PolygonPatch / Triangle / Rectangle */
649 : /* -------------------------------------------------------------------- */
650 15690 : if( EQUAL(pszBaseGeometry,"Polygon") ||
651 : EQUAL(pszBaseGeometry,"PolygonPatch") ||
652 : EQUAL(pszBaseGeometry,"Triangle") ||
653 : EQUAL(pszBaseGeometry,"Rectangle"))
654 : {
655 : const CPLXMLNode *psChild;
656 1230 : OGRPolygon *poPolygon = new OGRPolygon();
657 : OGRLinearRing *poRing;
658 :
659 : // Find outer ring.
660 1230 : psChild = FindBareXMLChild( psNode, "outerBoundaryIs" );
661 1230 : if (psChild == NULL)
662 1208 : psChild = FindBareXMLChild( psNode, "exterior");
663 :
664 1230 : psChild = GetChildElement(psChild);
665 1230 : if( psChild == NULL )
666 : {
667 : /* <gml:Polygon/> is invalid GML2, but valid GML3, so be tolerant */
668 4 : return poPolygon;
669 : }
670 :
671 : // Translate outer ring and add to polygon.
672 : poRing = (OGRLinearRing *)
673 : GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
674 1226 : nRecLevel + 1 );
675 1226 : if( poRing == NULL )
676 : {
677 2 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior ring");
678 2 : delete poPolygon;
679 2 : return NULL;
680 : }
681 :
682 1224 : if( !EQUAL(poRing->getGeometryName(),"LINEARRING") )
683 : {
684 : CPLError( CE_Failure, CPLE_AppDefined,
685 : "%s: Got %.500s geometry as outerBoundaryIs instead of LINEARRING.",
686 1 : pszBaseGeometry, poRing->getGeometryName() );
687 1 : delete poPolygon;
688 1 : delete poRing;
689 1 : return NULL;
690 : }
691 :
692 1223 : poPolygon->addRingDirectly( poRing );
693 :
694 : // Find all inner rings
695 4425 : for( psChild = psNode->psChild;
696 : psChild != NULL;
697 : psChild = psChild->psNext )
698 : {
699 3204 : if( psChild->eType == CXT_Element
700 : && (EQUAL(BareGMLElement(psChild->pszValue),"innerBoundaryIs") ||
701 : EQUAL(BareGMLElement(psChild->pszValue),"interior")))
702 : {
703 303 : const CPLXMLNode* psInteriorChild = GetChildElement(psChild);
704 303 : if (psInteriorChild != NULL)
705 : poRing = (OGRLinearRing *)
706 : GML2OGRGeometry_XMLNode( psInteriorChild, bGetSecondaryGeometryOption,
707 302 : nRecLevel + 1);
708 : else
709 1 : poRing = NULL;
710 303 : if (poRing == NULL)
711 : {
712 1 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring");
713 1 : delete poPolygon;
714 1 : return NULL;
715 : }
716 302 : if( !EQUAL(poRing->getGeometryName(),"LINEARRING") )
717 : {
718 : CPLError( CE_Failure, CPLE_AppDefined,
719 : "%s: Got %.500s geometry as innerBoundaryIs instead of LINEARRING.",
720 1 : pszBaseGeometry, poRing->getGeometryName() );
721 1 : delete poPolygon;
722 1 : delete poRing;
723 1 : return NULL;
724 : }
725 :
726 301 : poPolygon->addRingDirectly( poRing );
727 : }
728 : }
729 :
730 1221 : return poPolygon;
731 : }
732 :
733 : /* -------------------------------------------------------------------- */
734 : /* LinearRing */
735 : /* -------------------------------------------------------------------- */
736 14460 : if( EQUAL(pszBaseGeometry,"LinearRing") )
737 : {
738 1186 : OGRLinearRing *poLinearRing = new OGRLinearRing();
739 :
740 1186 : if( !ParseGMLCoordinates( psNode, poLinearRing ) )
741 : {
742 1 : delete poLinearRing;
743 1 : return NULL;
744 : }
745 :
746 1185 : return poLinearRing;
747 : }
748 :
749 : /* -------------------------------------------------------------------- */
750 : /* Ring GML3 */
751 : /* -------------------------------------------------------------------- */
752 13274 : if( EQUAL(pszBaseGeometry,"Ring") )
753 : {
754 344 : OGRLinearRing *poLinearRing = new OGRLinearRing();
755 : const CPLXMLNode *psChild;
756 :
757 3237 : for( psChild = psNode->psChild;
758 : psChild != NULL; psChild = psChild->psNext )
759 : {
760 2896 : if( psChild->eType == CXT_Element
761 : && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") )
762 : {
763 2895 : const CPLXMLNode* psCurveChild = GetChildElement(psChild);
764 : OGRLineString *poLS;
765 2895 : if (psCurveChild != NULL)
766 : poLS = (OGRLineString *)
767 : GML2OGRGeometry_XMLNode( psCurveChild, bGetSecondaryGeometryOption,
768 2894 : nRecLevel + 1);
769 : else
770 1 : poLS = NULL;
771 :
772 5788 : if( poLS == NULL
773 2893 : || wkbFlatten(poLS->getGeometryType()) != wkbLineString )
774 : {
775 3 : delete poLS;
776 3 : delete poLinearRing;
777 3 : return NULL;
778 : }
779 :
780 2892 : if( poLS->getNumPoints() < 2 )
781 : {
782 : // skip it
783 : }
784 2892 : else if( poLinearRing->getNumPoints() > 0
785 : && fabs(poLinearRing->getX(poLinearRing->getNumPoints()-1) - poLS->getX(0)) < 1e-14
786 : && fabs(poLinearRing->getY(poLinearRing->getNumPoints()-1) - poLS->getY(0)) < 1e-14
787 : && fabs(poLinearRing->getZ(poLinearRing->getNumPoints()-1) - poLS->getZ(0)) < 1e-14 )
788 : {
789 : // Skip the first point of the new linestring to avoid
790 : // invalidate duplicate points
791 2552 : poLinearRing->addSubLineString( poLS, 1 );
792 : }
793 : else
794 : {
795 : // Add the whole new line string
796 340 : poLinearRing->addSubLineString( poLS );
797 : }
798 :
799 2892 : delete poLS;
800 : }
801 : }
802 :
803 341 : return poLinearRing;
804 : }
805 :
806 : /* -------------------------------------------------------------------- */
807 : /* LineString */
808 : /* -------------------------------------------------------------------- */
809 12930 : if( EQUAL(pszBaseGeometry,"LineString")
810 : || EQUAL(pszBaseGeometry,"LineStringSegment") )
811 : {
812 3655 : OGRLineString *poLine = new OGRLineString();
813 :
814 3655 : if( !ParseGMLCoordinates( psNode, poLine ) )
815 : {
816 10 : delete poLine;
817 10 : return NULL;
818 : }
819 :
820 3645 : return poLine;
821 : }
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Arc/Circle : we approximate them by linear segments */
825 : /* -------------------------------------------------------------------- */
826 9275 : if( EQUAL(pszBaseGeometry,"Arc") ||
827 : EQUAL(pszBaseGeometry,"Circle") )
828 : {
829 18 : OGRLineString *poLine = new OGRLineString();
830 :
831 35 : if( !ParseGMLCoordinates( psNode, poLine ) ||
832 : poLine->getNumPoints() != 3 )
833 : {
834 2 : delete poLine;
835 2 : return NULL;
836 : }
837 :
838 16 : double x0 = poLine->getX(0);
839 16 : double y0 = poLine->getY(0);
840 16 : double x1 = poLine->getX(1);
841 16 : double y1 = poLine->getY(1);
842 16 : double x2 = poLine->getX(2);
843 16 : double y2 = poLine->getY(2);
844 16 : double dx01 = x1 - x0;
845 16 : double dy01 = y1 - y0;
846 16 : double dx12 = x2 - x1;
847 16 : double dy12 = y2 - y1;
848 16 : double c01 = dx01 * (x0 + x1) / 2 + dy01 * (y0 + y1) / 2;
849 16 : double c12 = dx12 * (x1 + x2) / 2 + dy12 * (y1 + y2) / 2;
850 16 : double det = dx01 * dy12 - dx12 * dy01;
851 16 : if (det == 0)
852 : {
853 1 : return poLine;
854 : }
855 15 : double cx = (c01 * dy12 - c12 * dy01) / det;
856 15 : double cy = (- c01 * dx12 + c12 * dx01) / det;
857 :
858 15 : double alpha0 = atan2(y0 - cy, x0 - cx);
859 15 : double alpha1 = atan2(y1 - cy, x1 - cx);
860 15 : double alpha2 = atan2(y2 - cy, x2 - cx);
861 : double alpha3;
862 15 : double R = sqrt((x0 - cx) * (x0 - cx) + (y0 - cy) * (y0 - cy));
863 :
864 : /* if det is negative, the orientation if clockwise */
865 15 : if (det < 0)
866 : {
867 9 : if (alpha1 > alpha0)
868 1 : alpha1 -= 2 * PI;
869 9 : if (alpha2 > alpha1)
870 2 : alpha2 -= 2 * PI;
871 9 : alpha3 = alpha0 - 2 * PI;
872 : }
873 : else
874 : {
875 6 : if (alpha1 < alpha0)
876 0 : alpha1 += 2 * PI;
877 6 : if (alpha2 < alpha1)
878 2 : alpha2 += 2 * PI;
879 6 : alpha3 = alpha0 + 2 * PI;
880 : }
881 :
882 : CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2 && alpha2 <= alpha3) ||
883 15 : (alpha0 >= alpha1 && alpha1 >= alpha2 && alpha2 >= alpha3));
884 :
885 15 : int nSign = (det >= 0) ? 1 : -1;
886 :
887 : double alpha;
888 15 : double dfStep = atof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")) / 180 * PI;
889 :
890 : // make sure the segments are not too short
891 15 : double dfMinStepLength = atof( CPLGetConfigOption("OGR_ARC_MINLENGTH","0") );
892 15 : if ( dfMinStepLength > 0.0 && dfStep * R < dfMinStepLength )
893 : {
894 : CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with segment length %lf at radius %lf; min segment length is %lf)",
895 : dfMinStepLength * 180.0 / PI / R,
896 : dfStep * 180.0 / PI,
897 : dfStep * R,
898 : R,
899 0 : dfMinStepLength );
900 0 : dfStep = dfMinStepLength / R;
901 : }
902 :
903 15 : if (dfStep < 4. / 180 * PI)
904 : {
905 : CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with length %lf at radius %lf).",
906 : 4. / 180 * PI,
907 : dfStep * 180.0 / PI,
908 : dfStep * R,
909 0 : R );
910 0 : dfStep = 4. / 180 * PI;
911 : }
912 :
913 15 : poLine->setNumPoints(0);
914 :
915 15 : dfStep *= nSign;
916 :
917 15 : poLine->addPoint(x0, y0);
918 :
919 38 : for(alpha = alpha0 + dfStep; (alpha - alpha1) * nSign < 0; alpha += dfStep)
920 : {
921 23 : poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha));
922 : }
923 :
924 15 : poLine->addPoint(x1, y1);
925 :
926 40 : for(alpha = alpha1 + dfStep; (alpha - alpha2) * nSign < 0; alpha += dfStep)
927 : {
928 25 : poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha));
929 : }
930 :
931 15 : if (EQUAL(pszBaseGeometry,"Circle"))
932 : {
933 3 : for(alpha = alpha2; (alpha - alpha3) * nSign < 0; alpha += dfStep)
934 : {
935 2 : poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha));
936 : }
937 1 : poLine->addPoint(x0, y0);
938 : }
939 : else
940 : {
941 14 : poLine->addPoint(x2, y2);
942 : }
943 :
944 15 : return poLine;
945 : }
946 :
947 : /* -------------------------------------------------------------------- */
948 : /* PointType */
949 : /* -------------------------------------------------------------------- */
950 9257 : if( EQUAL(pszBaseGeometry,"PointType")
951 : || EQUAL(pszBaseGeometry,"Point")
952 : || EQUAL(pszBaseGeometry,"ConnectionPoint") )
953 : {
954 580 : OGRPoint *poPoint = new OGRPoint();
955 :
956 580 : if( !ParseGMLCoordinates( psNode, poPoint ) )
957 : {
958 10 : delete poPoint;
959 10 : return NULL;
960 : }
961 :
962 570 : return poPoint;
963 : }
964 :
965 : /* -------------------------------------------------------------------- */
966 : /* Box */
967 : /* -------------------------------------------------------------------- */
968 8677 : if( EQUAL(pszBaseGeometry,"BoxType") || EQUAL(pszBaseGeometry,"Box") )
969 : {
970 3 : OGRLineString oPoints;
971 :
972 3 : if( !ParseGMLCoordinates( psNode, &oPoints ) )
973 1 : return NULL;
974 :
975 2 : if( oPoints.getNumPoints() < 2 )
976 1 : return NULL;
977 :
978 1 : OGRLinearRing *poBoxRing = new OGRLinearRing();
979 2 : OGRPolygon *poBoxPoly = new OGRPolygon();
980 :
981 1 : poBoxRing->setNumPoints( 5 );
982 : poBoxRing->setPoint(
983 1 : 0, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) );
984 : poBoxRing->setPoint(
985 1 : 1, oPoints.getX(1), oPoints.getY(0), oPoints.getZ(0) );
986 : poBoxRing->setPoint(
987 1 : 2, oPoints.getX(1), oPoints.getY(1), oPoints.getZ(1) );
988 : poBoxRing->setPoint(
989 1 : 3, oPoints.getX(0), oPoints.getY(1), oPoints.getZ(0) );
990 : poBoxRing->setPoint(
991 1 : 4, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) );
992 :
993 1 : poBoxPoly->addRingDirectly( poBoxRing );
994 :
995 1 : return poBoxPoly;
996 : }
997 :
998 : /* ------------------------const CPLXMLNode *psChild;-------------------------------------------- */
999 : /* MultiPolygon / MultiSurface / CompositeSurface */
1000 : /* */
1001 : /* For CompositeSurface, this is a very rough approximation to deal with*/
1002 : /* it as a MultiPolygon, because it can several faces of a 3D volume... */
1003 : /* -------------------------------------------------------------------- */
1004 8674 : if( EQUAL(pszBaseGeometry,"MultiPolygon") ||
1005 : EQUAL(pszBaseGeometry,"MultiSurface") ||
1006 : EQUAL(pszBaseGeometry,"CompositeSurface") )
1007 : {
1008 : const CPLXMLNode *psChild;
1009 747 : OGRMultiPolygon *poMPoly = new OGRMultiPolygon();
1010 :
1011 : // Iterate over children
1012 3063 : for( psChild = psNode->psChild;
1013 : psChild != NULL;
1014 : psChild = psChild->psNext )
1015 : {
1016 2319 : if( psChild->eType == CXT_Element
1017 : && (EQUAL(BareGMLElement(psChild->pszValue),"polygonMember") ||
1018 : EQUAL(BareGMLElement(psChild->pszValue),"surfaceMember")) )
1019 : {
1020 848 : const CPLXMLNode* psSurfaceChild = GetChildElement(psChild);
1021 : OGRPolygon *poPolygon;
1022 :
1023 848 : if (psSurfaceChild != NULL)
1024 : poPolygon = (OGRPolygon *)
1025 : GML2OGRGeometry_XMLNode( psSurfaceChild, bGetSecondaryGeometryOption,
1026 847 : nRecLevel + 1);
1027 : else
1028 1 : poPolygon = NULL;
1029 :
1030 848 : if( poPolygon == NULL )
1031 : {
1032 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s",
1033 2 : BareGMLElement(psChild->pszValue));
1034 2 : delete poMPoly;
1035 2 : return NULL;
1036 : }
1037 :
1038 846 : if( !EQUAL(poPolygon->getGeometryName(),"POLYGON") )
1039 : {
1040 : CPLError( CE_Failure, CPLE_AppDefined,
1041 : "Got %.500s geometry as polygonMember instead of MULTIPOLYGON.",
1042 1 : poPolygon->getGeometryName() );
1043 1 : delete poPolygon;
1044 1 : delete poMPoly;
1045 1 : return NULL;
1046 : }
1047 :
1048 845 : poMPoly->addGeometryDirectly( poPolygon );
1049 : }
1050 1471 : else if (psChild->eType == CXT_Element
1051 : && EQUAL(BareGMLElement(psChild->pszValue),"surfaceMembers") )
1052 : {
1053 : const CPLXMLNode *psChild2;
1054 9 : for( psChild2 = psChild->psChild;
1055 : psChild2 != NULL;
1056 : psChild2 = psChild2->psNext )
1057 : {
1058 5 : if( psChild2->eType == CXT_Element
1059 : && (EQUAL(BareGMLElement(psChild2->pszValue),"Surface") ||
1060 : EQUAL(BareGMLElement(psChild2->pszValue),"Polygon") ||
1061 : EQUAL(BareGMLElement(psChild2->pszValue),"PolygonPatch")) )
1062 : {
1063 : OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption,
1064 3 : nRecLevel + 1);
1065 3 : if (poGeom == NULL)
1066 : {
1067 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s",
1068 0 : BareGMLElement(psChild2->pszValue));
1069 0 : delete poMPoly;
1070 0 : return NULL;
1071 : }
1072 :
1073 3 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
1074 : {
1075 2 : poMPoly->addGeometryDirectly( (OGRPolygon*) poGeom );
1076 : }
1077 1 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
1078 : {
1079 1 : OGRMultiPolygon* poMPoly2 = (OGRMultiPolygon*) poGeom;
1080 : int i;
1081 3 : for(i=0;i<poMPoly2->getNumGeometries();i++)
1082 : {
1083 2 : poMPoly->addGeometry(poMPoly2->getGeometryRef(i));
1084 : }
1085 1 : delete poGeom;
1086 : }
1087 : else
1088 : {
1089 : CPLError( CE_Failure, CPLE_AppDefined,
1090 : "Got %.500s geometry as polygonMember instead of POLYGON/MULTIPOLYGON.",
1091 0 : poGeom->getGeometryName() );
1092 0 : delete poGeom;
1093 0 : delete poMPoly;
1094 0 : return NULL;
1095 : }
1096 : }
1097 : }
1098 : }
1099 : }
1100 :
1101 744 : return poMPoly;
1102 : }
1103 :
1104 : /* -------------------------------------------------------------------- */
1105 : /* MultiPoint */
1106 : /* -------------------------------------------------------------------- */
1107 7927 : if( EQUAL(pszBaseGeometry,"MultiPoint") )
1108 : {
1109 : const CPLXMLNode *psChild;
1110 20 : OGRMultiPoint *poMP = new OGRMultiPoint();
1111 :
1112 : // collect points.
1113 57 : for( psChild = psNode->psChild;
1114 : psChild != NULL;
1115 : psChild = psChild->psNext )
1116 : {
1117 40 : if( psChild->eType == CXT_Element
1118 : && EQUAL(BareGMLElement(psChild->pszValue),"pointMember") )
1119 : {
1120 19 : const CPLXMLNode* psPointChild = GetChildElement(psChild);
1121 : OGRPoint *poPoint;
1122 :
1123 19 : if (psPointChild != NULL)
1124 : poPoint = (OGRPoint *)
1125 : GML2OGRGeometry_XMLNode( psPointChild, bGetSecondaryGeometryOption,
1126 18 : nRecLevel + 1);
1127 : else
1128 1 : poPoint = NULL;
1129 37 : if( poPoint == NULL
1130 18 : || wkbFlatten(poPoint->getGeometryType()) != wkbPoint )
1131 : {
1132 : CPLError( CE_Failure, CPLE_AppDefined,
1133 : "MultiPoint: Got %.500s geometry as pointMember instead of POINT",
1134 2 : poPoint ? poPoint->getGeometryName() : "NULL" );
1135 2 : delete poPoint;
1136 2 : delete poMP;
1137 2 : return NULL;
1138 : }
1139 :
1140 17 : poMP->addGeometryDirectly( poPoint );
1141 : }
1142 21 : else if (psChild->eType == CXT_Element
1143 : && EQUAL(BareGMLElement(psChild->pszValue),"pointMembers") )
1144 : {
1145 : const CPLXMLNode *psChild2;
1146 8 : for( psChild2 = psChild->psChild;
1147 : psChild2 != NULL;
1148 : psChild2 = psChild2->psNext )
1149 : {
1150 5 : if( psChild2->eType == CXT_Element
1151 : && (EQUAL(BareGMLElement(psChild2->pszValue),"Point")) )
1152 : {
1153 : OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption,
1154 3 : nRecLevel + 1);
1155 3 : if (poGeom == NULL)
1156 : {
1157 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s",
1158 1 : BareGMLElement(psChild2->pszValue));
1159 1 : delete poMP;
1160 1 : return NULL;
1161 : }
1162 :
1163 2 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1164 : {
1165 2 : poMP->addGeometryDirectly( (OGRPoint *)poGeom );
1166 : }
1167 : else
1168 : {
1169 : CPLError( CE_Failure, CPLE_AppDefined,
1170 : "Got %.500s geometry as pointMember instead of POINT.",
1171 0 : poGeom->getGeometryName() );
1172 0 : delete poGeom;
1173 0 : delete poMP;
1174 0 : return NULL;
1175 : }
1176 : }
1177 : }
1178 : }
1179 : }
1180 :
1181 17 : return poMP;
1182 : }
1183 :
1184 : /* -------------------------------------------------------------------- */
1185 : /* MultiLineString */
1186 : /* -------------------------------------------------------------------- */
1187 7907 : if( EQUAL(pszBaseGeometry,"MultiLineString") )
1188 : {
1189 : const CPLXMLNode *psChild;
1190 6 : OGRMultiLineString *poMLS = new OGRMultiLineString();
1191 :
1192 : // collect lines
1193 13 : for( psChild = psNode->psChild;
1194 : psChild != NULL;
1195 : psChild = psChild->psNext )
1196 : {
1197 9 : if( psChild->eType == CXT_Element
1198 : && EQUAL(BareGMLElement(psChild->pszValue),"lineStringMember") )
1199 : {
1200 7 : const CPLXMLNode* psLineStringChild = GetChildElement(psChild);
1201 : OGRGeometry *poGeom;
1202 :
1203 7 : if (psLineStringChild != NULL)
1204 : poGeom = GML2OGRGeometry_XMLNode( psLineStringChild, bGetSecondaryGeometryOption,
1205 6 : nRecLevel + 1);
1206 : else
1207 1 : poGeom = NULL;
1208 13 : if( poGeom == NULL
1209 6 : || wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
1210 : {
1211 : CPLError( CE_Failure, CPLE_AppDefined,
1212 : "MultiLineString: Got %.500s geometry as Member instead of LINESTRING.",
1213 2 : poGeom ? poGeom->getGeometryName() : "NULL" );
1214 2 : delete poGeom;
1215 2 : delete poMLS;
1216 2 : return NULL;
1217 : }
1218 :
1219 5 : poMLS->addGeometryDirectly( poGeom );
1220 : }
1221 : }
1222 :
1223 4 : return poMLS;
1224 : }
1225 :
1226 :
1227 : /* -------------------------------------------------------------------- */
1228 : /* MultiCurve / CompositeCurve */
1229 : /* -------------------------------------------------------------------- */
1230 7901 : if( EQUAL(pszBaseGeometry,"MultiCurve") ||
1231 : EQUAL(pszBaseGeometry,"CompositeCurve") )
1232 : {
1233 : const CPLXMLNode *psChild, *psCurve;
1234 22 : OGRMultiLineString *poMLS = new OGRMultiLineString();
1235 :
1236 : // collect curveMembers
1237 57 : for( psChild = psNode->psChild;
1238 : psChild != NULL;
1239 : psChild = psChild->psNext )
1240 : {
1241 41 : if( psChild->eType == CXT_Element
1242 : && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") )
1243 : {
1244 : OGRGeometry *poGeom;
1245 :
1246 : // There can be only one curve under a curveMember.
1247 : // Currently "Curve" and "LineString" are handled.
1248 18 : psCurve = FindBareXMLChild( psChild, "Curve" );
1249 18 : if( psCurve == NULL )
1250 13 : psCurve = FindBareXMLChild( psChild, "LineString" );
1251 18 : if( psCurve == NULL )
1252 : {
1253 : CPLError( CE_Failure, CPLE_AppDefined,
1254 2 : "Failed to get curve element in curveMember" );
1255 2 : delete poMLS;
1256 2 : return NULL;
1257 : }
1258 : poGeom = GML2OGRGeometry_XMLNode( psCurve, bGetSecondaryGeometryOption,
1259 16 : nRecLevel + 1);
1260 29 : if( poGeom == NULL ||
1261 13 : ( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) )
1262 : {
1263 : CPLError( CE_Failure, CPLE_AppDefined,
1264 : "MultiCurve: Got %.500s geometry as Member instead of LINESTRING.",
1265 3 : poGeom ? poGeom->getGeometryName() : "NULL" );
1266 3 : if( poGeom != NULL ) delete poGeom;
1267 3 : delete poMLS;
1268 3 : return NULL;
1269 : }
1270 :
1271 13 : poMLS->addGeometryDirectly( (OGRLineString *)poGeom );
1272 : }
1273 23 : else if (psChild->eType == CXT_Element
1274 : && EQUAL(BareGMLElement(psChild->pszValue),"curveMembers") )
1275 : {
1276 : const CPLXMLNode *psChild2;
1277 9 : for( psChild2 = psChild->psChild;
1278 : psChild2 != NULL;
1279 : psChild2 = psChild2->psNext )
1280 : {
1281 5 : if( psChild2->eType == CXT_Element
1282 : && (EQUAL(BareGMLElement(psChild2->pszValue),"LineString")) )
1283 : {
1284 : OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption,
1285 3 : nRecLevel + 1);
1286 3 : if (poGeom == NULL)
1287 : {
1288 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s",
1289 1 : BareGMLElement(psChild2->pszValue));
1290 1 : delete poMLS;
1291 1 : return NULL;
1292 : }
1293 :
1294 2 : if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1295 : {
1296 2 : poMLS->addGeometryDirectly( (OGRLineString *)poGeom );
1297 : }
1298 : else
1299 : {
1300 : CPLError( CE_Failure, CPLE_AppDefined,
1301 : "Got %.500s geometry as curveMember instead of LINESTRING.",
1302 0 : poGeom->getGeometryName() );
1303 0 : delete poGeom;
1304 0 : delete poMLS;
1305 0 : return NULL;
1306 : }
1307 : }
1308 : }
1309 : }
1310 : }
1311 16 : return poMLS;
1312 : }
1313 :
1314 : /* -------------------------------------------------------------------- */
1315 : /* Curve */
1316 : /* -------------------------------------------------------------------- */
1317 7879 : if( EQUAL(pszBaseGeometry,"Curve") )
1318 : {
1319 : const CPLXMLNode *psChild;
1320 :
1321 3228 : psChild = FindBareXMLChild( psNode, "segments");
1322 3228 : if( psChild == NULL )
1323 : {
1324 : CPLError( CE_Failure, CPLE_AppDefined,
1325 5 : "GML3 Curve geometry lacks segments element." );
1326 5 : return NULL;
1327 : }
1328 :
1329 : OGRGeometry *poGeom;
1330 :
1331 : poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
1332 3223 : nRecLevel + 1);
1333 6446 : if( poGeom == NULL ||
1334 3223 : wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
1335 : {
1336 : CPLError( CE_Failure, CPLE_AppDefined,
1337 : "Curve: Got %.500s geometry as Member instead of segments.",
1338 0 : poGeom ? poGeom->getGeometryName() : "NULL" );
1339 0 : if( poGeom != NULL ) delete poGeom;
1340 0 : return NULL;
1341 : }
1342 :
1343 3223 : return poGeom;
1344 : }
1345 :
1346 : /* -------------------------------------------------------------------- */
1347 : /* segments */
1348 : /* -------------------------------------------------------------------- */
1349 4651 : if( EQUAL(pszBaseGeometry,"segments") )
1350 : {
1351 : const CPLXMLNode *psChild;
1352 3228 : OGRLineString *poLS = new OGRLineString();
1353 :
1354 6458 : for( psChild = psNode->psChild;
1355 : psChild != NULL;
1356 : psChild = psChild->psNext )
1357 :
1358 : {
1359 3230 : if( psChild->eType == CXT_Element
1360 : && (EQUAL(BareGMLElement(psChild->pszValue),"LineStringSegment") ||
1361 : EQUAL(BareGMLElement(psChild->pszValue),"Arc") ||
1362 : EQUAL(BareGMLElement(psChild->pszValue),"Circle")) )
1363 : {
1364 : OGRGeometry *poGeom;
1365 :
1366 : poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
1367 3226 : nRecLevel + 1);
1368 6449 : if( poGeom != NULL &&
1369 3223 : wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
1370 : {
1371 : CPLError( CE_Failure, CPLE_AppDefined,
1372 : "segments: Got %.500s geometry as Member instead of LINESTRING.",
1373 0 : poGeom ? poGeom->getGeometryName() : "NULL" );
1374 0 : delete poGeom;
1375 0 : delete poLS;
1376 0 : return NULL;
1377 : }
1378 3226 : if( poGeom != NULL )
1379 : {
1380 3223 : OGRLineString *poAddLS = (OGRLineString *)poGeom;
1381 3223 : if( poLS->getNumPoints() > 0 && poAddLS->getNumPoints() > 0
1382 : && fabs(poLS->getX(poLS->getNumPoints()-1)
1383 : - poAddLS->getX(0)) < 1e-14
1384 : && fabs(poLS->getY(poLS->getNumPoints()-1)
1385 : - poAddLS->getY(0)) < 1e-14
1386 : && fabs(poLS->getZ(poLS->getNumPoints()-1)
1387 : - poAddLS->getZ(0)) < 1e-14)
1388 : {
1389 : // Skip the first point of the new linestring to avoid
1390 : // invalidate duplicate points (#4451)
1391 3 : poLS->addSubLineString( poAddLS, 1 );
1392 : }
1393 : else
1394 : {
1395 : // Add the whole new line string
1396 3220 : poLS->addSubLineString( poAddLS );
1397 : }
1398 3223 : delete poGeom;
1399 : }
1400 : }
1401 : }
1402 :
1403 3228 : return poLS;
1404 : }
1405 :
1406 : /* -------------------------------------------------------------------- */
1407 : /* MultiGeometry */
1408 : /* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is */
1409 : /* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */
1410 : /* tolerant with the non compliant files we produced... */
1411 : /* -------------------------------------------------------------------- */
1412 1423 : if( EQUAL(pszBaseGeometry,"MultiGeometry") ||
1413 : EQUAL(pszBaseGeometry,"GeometryCollection") )
1414 : {
1415 : const CPLXMLNode *psChild;
1416 74 : OGRGeometryCollection *poGC = new OGRGeometryCollection();
1417 :
1418 : // collect geoms
1419 125 : for( psChild = psNode->psChild;
1420 : psChild != NULL;
1421 : psChild = psChild->psNext )
1422 : {
1423 85 : if( psChild->eType == CXT_Element
1424 : && EQUAL(BareGMLElement(psChild->pszValue),"geometryMember") )
1425 : {
1426 79 : const CPLXMLNode* psGeometryChild = GetChildElement(psChild);
1427 : OGRGeometry *poGeom;
1428 :
1429 79 : if (psGeometryChild != NULL)
1430 : poGeom = GML2OGRGeometry_XMLNode( psGeometryChild, bGetSecondaryGeometryOption,
1431 78 : nRecLevel + 1 );
1432 : else
1433 1 : poGeom = NULL;
1434 79 : if( poGeom == NULL )
1435 : {
1436 : CPLError( CE_Failure, CPLE_AppDefined,
1437 34 : "GeometryCollection: Failed to get geometry in geometryMember" );
1438 34 : delete poGeom;
1439 34 : delete poGC;
1440 34 : return NULL;
1441 : }
1442 :
1443 45 : poGC->addGeometryDirectly( poGeom );
1444 : }
1445 : }
1446 :
1447 40 : return poGC;
1448 : }
1449 :
1450 : /* -------------------------------------------------------------------- */
1451 : /* Directed Edge */
1452 : /* -------------------------------------------------------------------- */
1453 1349 : if( EQUAL(pszBaseGeometry,"directedEdge") )
1454 : {
1455 : const CPLXMLNode *psEdge,
1456 : *psdirectedNode,
1457 : *psNodeElement,
1458 : *pspointProperty,
1459 : *psPoint,
1460 : *psCurveProperty,
1461 : *psCurve;
1462 692 : int bEdgeOrientation = TRUE,
1463 692 : bNodeOrientation = TRUE;
1464 : OGRGeometry *poGeom;
1465 : OGRLineString *poLineString;
1466 692 : OGRPoint *poPositiveNode = NULL, *poNegativeNode = NULL;
1467 : OGRMultiPoint *poMP;
1468 :
1469 692 : bEdgeOrientation = GetElementOrientation(psNode);
1470 :
1471 : //collect edge
1472 692 : psEdge = FindBareXMLChild(psNode,"Edge");
1473 692 : if( psEdge == NULL )
1474 : {
1475 : CPLError( CE_Failure, CPLE_AppDefined,
1476 0 : "Failed to get Edge element in directedEdge" );
1477 0 : return NULL;
1478 : }
1479 :
1480 692 : if( bGetSecondaryGeometry )
1481 : {
1482 0 : psdirectedNode = FindBareXMLChild(psEdge,"directedNode");
1483 0 : if( psdirectedNode == NULL ) goto nonode;
1484 :
1485 0 : bNodeOrientation = GetElementOrientation( psdirectedNode );
1486 :
1487 0 : psNodeElement = FindBareXMLChild(psdirectedNode,"Node");
1488 0 : if( psNodeElement == NULL ) goto nonode;
1489 :
1490 0 : pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty");
1491 0 : if( pspointProperty == NULL )
1492 0 : pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty");
1493 0 : if( pspointProperty == NULL ) goto nonode;
1494 :
1495 0 : psPoint = FindBareXMLChild(pspointProperty,"Point");
1496 0 : if( psPoint == NULL )
1497 0 : psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint");
1498 0 : if( psPoint == NULL ) goto nonode;
1499 :
1500 : poGeom = GML2OGRGeometry_XMLNode( psPoint, bGetSecondaryGeometryOption,
1501 0 : nRecLevel + 1, TRUE );
1502 0 : if( poGeom == NULL
1503 0 : || wkbFlatten(poGeom->getGeometryType()) != wkbPoint )
1504 : {
1505 : /* CPLError( CE_Failure, CPLE_AppDefined,
1506 : "Got %.500s geometry as Member instead of POINT.",
1507 : poGeom ? poGeom->getGeometryName() : "NULL" );*/
1508 0 : if( poGeom != NULL) delete poGeom;
1509 0 : goto nonode;
1510 : }
1511 :
1512 0 : if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation )
1513 0 : poPositiveNode = (OGRPoint *)poGeom;
1514 : else
1515 0 : poNegativeNode = (OGRPoint *)poGeom;
1516 :
1517 : // look for the other node
1518 0 : psdirectedNode = psdirectedNode->psNext;
1519 0 : while( psdirectedNode != NULL &&
1520 : !EQUAL( psdirectedNode->pszValue, "directedNode" ) )
1521 0 : psdirectedNode = psdirectedNode->psNext;
1522 0 : if( psdirectedNode == NULL ) goto nonode;
1523 :
1524 0 : if( GetElementOrientation( psdirectedNode ) == bNodeOrientation )
1525 0 : goto nonode;
1526 :
1527 0 : psNodeElement = FindBareXMLChild(psEdge,"Node");
1528 0 : if( psNodeElement == NULL ) goto nonode;
1529 :
1530 0 : pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty");
1531 0 : if( pspointProperty == NULL )
1532 0 : pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty");
1533 0 : if( pspointProperty == NULL ) goto nonode;
1534 :
1535 0 : psPoint = FindBareXMLChild(pspointProperty,"Point");
1536 0 : if( psPoint == NULL )
1537 0 : psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint");
1538 0 : if( psPoint == NULL ) goto nonode;
1539 :
1540 : poGeom = GML2OGRGeometry_XMLNode( psPoint, bGetSecondaryGeometryOption,
1541 0 : nRecLevel + 1, TRUE );
1542 0 : if( poGeom == NULL
1543 0 : || wkbFlatten(poGeom->getGeometryType()) != wkbPoint )
1544 : {
1545 : /* CPLError( CE_Failure, CPLE_AppDefined,
1546 : "Got %.500s geometry as Member instead of POINT.",
1547 : poGeom ? poGeom->getGeometryName() : "NULL" );*/
1548 0 : if( poGeom != NULL) delete poGeom;
1549 0 : goto nonode;
1550 : }
1551 :
1552 0 : if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation )
1553 0 : poNegativeNode = (OGRPoint *)poGeom;
1554 : else
1555 0 : poPositiveNode = (OGRPoint *)poGeom;
1556 :
1557 0 : poMP = new OGRMultiPoint();
1558 0 : poMP->addGeometryDirectly( poNegativeNode );
1559 0 : poMP->addGeometryDirectly( poPositiveNode );
1560 :
1561 0 : return poMP;
1562 :
1563 : nonode:;
1564 : }
1565 :
1566 : // collect curveproperty
1567 692 : psCurveProperty = FindBareXMLChild(psEdge,"curveProperty");
1568 692 : if( psCurveProperty == NULL )
1569 : {
1570 : CPLError( CE_Failure, CPLE_AppDefined,
1571 0 : "directedEdge: Failed to get curveProperty in Edge" );
1572 0 : return NULL;
1573 : }
1574 :
1575 692 : psCurve = FindBareXMLChild(psCurveProperty,"LineString");
1576 692 : if( psCurve == NULL )
1577 320 : psCurve = FindBareXMLChild(psCurveProperty,"Curve");
1578 692 : if( psCurve == NULL )
1579 : {
1580 : CPLError( CE_Failure, CPLE_AppDefined,
1581 0 : "directedEdge: Failed to get LineString or Curve tag in curveProperty" );
1582 0 : return NULL;
1583 : }
1584 :
1585 : poLineString = (OGRLineString *)GML2OGRGeometry_XMLNode( psCurve, bGetSecondaryGeometryOption,
1586 692 : nRecLevel + 1, TRUE );
1587 1384 : if( poLineString == NULL
1588 692 : || wkbFlatten(poLineString->getGeometryType()) != wkbLineString )
1589 : {
1590 : CPLError( CE_Failure, CPLE_AppDefined,
1591 : "Got %.500s geometry as Member instead of LINESTRING.",
1592 0 : poLineString ? poLineString->getGeometryName() : "NULL" );
1593 0 : if( poLineString != NULL )
1594 0 : delete poLineString;
1595 0 : return NULL;
1596 : }
1597 :
1598 692 : if( bGetSecondaryGeometry )
1599 : {
1600 : // choose a point based on the orientation
1601 0 : poNegativeNode = new OGRPoint();
1602 0 : poPositiveNode = new OGRPoint();
1603 0 : if( bEdgeOrientation == bOrientation )
1604 : {
1605 0 : poLineString->StartPoint( poNegativeNode );
1606 0 : poLineString->EndPoint( poPositiveNode );
1607 : }
1608 : else
1609 : {
1610 0 : poLineString->StartPoint( poPositiveNode );
1611 0 : poLineString->EndPoint( poNegativeNode );
1612 : }
1613 0 : delete poLineString;
1614 :
1615 0 : poMP = new OGRMultiPoint();
1616 0 : poMP->addGeometryDirectly( poNegativeNode );
1617 0 : poMP->addGeometryDirectly( poPositiveNode );
1618 :
1619 0 : return poMP;
1620 : }
1621 :
1622 : // correct orientation of the line string
1623 692 : if( bEdgeOrientation != bOrientation )
1624 : {
1625 219 : int iStartCoord = 0, iEndCoord = poLineString->getNumPoints() - 1;
1626 219 : OGRPoint *poTempStartPoint = new OGRPoint();
1627 438 : OGRPoint *poTempEndPoint = new OGRPoint();
1628 719 : while( iStartCoord < iEndCoord )
1629 : {
1630 281 : poLineString->getPoint( iStartCoord, poTempStartPoint );
1631 281 : poLineString->getPoint( iEndCoord, poTempEndPoint );
1632 281 : poLineString->setPoint( iStartCoord, poTempEndPoint );
1633 281 : poLineString->setPoint( iEndCoord, poTempStartPoint );
1634 281 : iStartCoord++;
1635 281 : iEndCoord--;
1636 : }
1637 219 : delete poTempStartPoint;
1638 219 : delete poTempEndPoint;
1639 : }
1640 692 : return poLineString;
1641 : }
1642 :
1643 : /* -------------------------------------------------------------------- */
1644 : /* TopoCurve */
1645 : /* -------------------------------------------------------------------- */
1646 657 : if( EQUAL(pszBaseGeometry,"TopoCurve") )
1647 : {
1648 : const CPLXMLNode *psChild;
1649 226 : OGRMultiLineString *poMLS = NULL;
1650 226 : OGRMultiPoint *poMP = NULL;
1651 :
1652 226 : if( bGetSecondaryGeometry )
1653 0 : poMP = new OGRMultiPoint();
1654 : else
1655 226 : poMLS = new OGRMultiLineString();
1656 :
1657 : // collect directedEdges
1658 472 : for( psChild = psNode->psChild;
1659 : psChild != NULL;
1660 : psChild = psChild->psNext )
1661 : {
1662 246 : if( psChild->eType == CXT_Element
1663 : && EQUAL(BareGMLElement(psChild->pszValue),"directedEdge"))
1664 : {
1665 : OGRGeometry *poGeom;
1666 :
1667 : poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
1668 246 : nRecLevel + 1 );
1669 246 : if( poGeom == NULL )
1670 : {
1671 : CPLError( CE_Failure, CPLE_AppDefined,
1672 0 : "Failed to get geometry in directedEdge" );
1673 0 : delete poGeom;
1674 0 : if( bGetSecondaryGeometry )
1675 0 : delete poMP;
1676 : else
1677 0 : delete poMLS;
1678 0 : return NULL;
1679 : }
1680 :
1681 : //Add the two points corresponding to the two nodes to poMP
1682 246 : if( bGetSecondaryGeometry &&
1683 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint )
1684 : {
1685 : //TODO: TopoCurve geometries with more than one
1686 : // directedEdge elements were not tested.
1687 0 : if( poMP->getNumGeometries() <= 0 ||
1688 0 : !(poMP->getGeometryRef( poMP->getNumGeometries() - 1 )->Equals(((OGRMultiPoint *)poGeom)->getGeometryRef( 0 ) ) ))
1689 : {
1690 : poMP->addGeometry(
1691 0 : ( (OGRMultiPoint *)poGeom )->getGeometryRef( 0 ) );
1692 : }
1693 : poMP->addGeometry(
1694 0 : ( (OGRMultiPoint *)poGeom )->getGeometryRef( 1 ) );
1695 0 : delete poGeom;
1696 : }
1697 492 : else if( !bGetSecondaryGeometry &&
1698 246 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
1699 : {
1700 246 : poMLS->addGeometryDirectly( poGeom );
1701 : }
1702 : else
1703 : {
1704 : CPLError( CE_Failure, CPLE_AppDefined,
1705 : "Got %.500s geometry as Member instead of %s.",
1706 0 : poGeom ? poGeom->getGeometryName() : "NULL",
1707 0 : bGetSecondaryGeometry?"MULTIPOINT":"LINESTRING");
1708 0 : delete poGeom;
1709 0 : if( bGetSecondaryGeometry )
1710 0 : delete poMP;
1711 : else
1712 0 : delete poMLS;
1713 0 : return NULL;
1714 : }
1715 : }
1716 : }
1717 :
1718 226 : if( bGetSecondaryGeometry )
1719 0 : return poMP;
1720 : else
1721 226 : return poMLS;
1722 : }
1723 :
1724 : /* -------------------------------------------------------------------- */
1725 : /* TopoSurface */
1726 : /* -------------------------------------------------------------------- */
1727 431 : if( EQUAL(pszBaseGeometry,"TopoSurface") )
1728 : {
1729 : /****************************************************************/
1730 : /* applying the FaceHoleNegative = FALSE rules */
1731 : /* */
1732 : /* - each <TopoSurface> is expected to represent a MultiPolygon */
1733 : /* - each <Face> is expected to represent a distinct Polygon, */
1734 : /* this including any possible Interior Ring (holes); */
1735 : /* orientation="+/-" plays no role at all to identify "holes" */
1736 : /* - each <Edge> within a <Face> may indifferently represent */
1737 : /* an element of the Exterior or Interior Boundary; relative */
1738 : /* order of <Egdes> is absolutely irrelevant. */
1739 : /****************************************************************/
1740 : /* Contributor: Alessandro Furieri, a.furieri@lqt.it */
1741 : /* Developed for Faunalia (http://www.faunalia.it) */
1742 : /* with funding from Regione Toscana - */
1743 : /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE */
1744 : /****************************************************************/
1745 45 : if(bFaceHoleNegative != TRUE)
1746 : {
1747 34 : if( bGetSecondaryGeometry )
1748 0 : return NULL;
1749 :
1750 : #ifndef HAVE_GEOS
1751 : static int bWarningAlreadyEmitted = FALSE;
1752 : if (!bWarningAlreadyEmitted)
1753 : {
1754 : CPLError(CE_Failure, CPLE_AppDefined,
1755 : "Interpreating that GML TopoSurface geometry requires GDAL to be built with GEOS support.\n"
1756 : "As a workaround, you can try defining the GML_FACE_HOLE_NEGATIVE configuration option\n"
1757 : "to YES, so that the 'old' interpretation algorithm is used. But be warned that\n"
1758 : "the result might be incorrect.\n");
1759 : bWarningAlreadyEmitted = TRUE;
1760 : }
1761 : return NULL;
1762 : #else
1763 : const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild;
1764 34 : OGRMultiPolygon *poTS = new OGRMultiPolygon();
1765 :
1766 : // collect directed faces
1767 100 : for( psChild = psNode->psChild;
1768 : psChild != NULL;
1769 : psChild = psChild->psNext )
1770 : {
1771 66 : if( psChild->eType == CXT_Element
1772 : && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") )
1773 : {
1774 : // collect next face (psChild->psChild)
1775 66 : psFaceChild = GetChildElement(psChild);
1776 :
1777 132 : while( psFaceChild != NULL &&
1778 : !(psFaceChild->eType == CXT_Element &&
1779 : EQUAL(BareGMLElement(psFaceChild->pszValue),"Face")) )
1780 0 : psFaceChild = psFaceChild->psNext;
1781 :
1782 66 : if( psFaceChild == NULL )
1783 0 : continue;
1784 :
1785 66 : OGRMultiLineString *poCollectedGeom = new OGRMultiLineString();
1786 :
1787 : // collect directed edges of the face
1788 486 : for( psDirectedEdgeChild = psFaceChild->psChild;
1789 : psDirectedEdgeChild != NULL;
1790 : psDirectedEdgeChild = psDirectedEdgeChild->psNext )
1791 : {
1792 420 : if( psDirectedEdgeChild->eType == CXT_Element &&
1793 : EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") )
1794 : {
1795 : OGRGeometry *poEdgeGeom;
1796 :
1797 : poEdgeGeom = GML2OGRGeometry_XMLNode( psDirectedEdgeChild,
1798 : bGetSecondaryGeometryOption,
1799 354 : TRUE );
1800 :
1801 708 : if( poEdgeGeom == NULL ||
1802 354 : wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString )
1803 : {
1804 : CPLError( CE_Failure, CPLE_AppDefined,
1805 0 : "Failed to get geometry in directedEdge" );
1806 0 : delete poEdgeGeom;
1807 0 : delete poCollectedGeom;
1808 0 : delete poTS;
1809 0 : return NULL;
1810 : }
1811 :
1812 354 : poCollectedGeom->addGeometryDirectly( poEdgeGeom );
1813 : }
1814 : }
1815 :
1816 66 : OGRGeometry *poFaceCollectionGeom = NULL;
1817 66 : OGRPolygon *poFaceGeom = NULL;
1818 :
1819 : //#ifdef HAVE_GEOS
1820 66 : poFaceCollectionGeom = poCollectedGeom->Polygonize();
1821 66 : if( poFaceCollectionGeom == NULL )
1822 : {
1823 : CPLError( CE_Failure, CPLE_AppDefined,
1824 0 : "Failed to assemble Edges in Face" );
1825 0 : delete poCollectedGeom;
1826 0 : delete poTS;
1827 0 : return NULL;
1828 : }
1829 :
1830 66 : poFaceGeom = GML2FaceExtRing( poFaceCollectionGeom );
1831 : //#else
1832 : // poFaceGeom = (OGRPolygon*) OGRBuildPolygonFromEdges(
1833 : // (OGRGeometryH) poCollectedGeom,
1834 : // FALSE, TRUE, 0, NULL);
1835 : //#endif
1836 :
1837 66 : if( poFaceGeom == NULL )
1838 : {
1839 : CPLError( CE_Failure, CPLE_AppDefined,
1840 0 : "Failed to build Polygon for Face" );
1841 0 : delete poCollectedGeom;
1842 0 : delete poTS;
1843 0 : return NULL;
1844 : }
1845 : else
1846 : {
1847 66 : int iCount = poTS->getNumGeometries();
1848 66 : if( iCount == 0)
1849 : {
1850 : /* inserting the first Polygon */
1851 34 : poTS->addGeometryDirectly( poFaceGeom );
1852 : }
1853 : else
1854 : {
1855 : /* using Union to add the current Polygon */
1856 32 : OGRGeometry *poUnion = poTS->Union( poFaceGeom );
1857 32 : delete poFaceGeom;
1858 32 : delete poTS;
1859 32 : if( poUnion == NULL )
1860 : {
1861 : CPLError( CE_Failure, CPLE_AppDefined,
1862 0 : "Failed Union for TopoSurface" );
1863 0 : return NULL;
1864 : }
1865 32 : poTS = (OGRMultiPolygon *)poUnion;
1866 : }
1867 : }
1868 66 : delete poFaceCollectionGeom;
1869 66 : delete poCollectedGeom;
1870 : }
1871 : }
1872 :
1873 34 : if( wkbFlatten( poTS->getGeometryType()) == wkbPolygon )
1874 : {
1875 : /* forcing to be a MultiPolygon */
1876 8 : OGRGeometry *poOldTS = poTS;
1877 8 : poTS = new OGRMultiPolygon();
1878 8 : poTS->addGeometryDirectly(poOldTS);
1879 : }
1880 :
1881 34 : return poTS;
1882 : #endif // HAVE_GEOS
1883 : }
1884 :
1885 : /****************************************************************/
1886 : /* applying the FaceHoleNegative = TRUE rules */
1887 : /* */
1888 : /* - each <TopoSurface> is expected to represent a MultiPolygon */
1889 : /* - any <Face> declaring orientation="+" is expected to */
1890 : /* represent an Exterior Ring (no holes are allowed) */
1891 : /* - any <Face> declaring orientation="-" is expected to */
1892 : /* represent an Interior Ring (hole) belonging to the latest */
1893 : /* Exterior Ring. */
1894 : /* - <Egdes> within the same <Face> are expected to be */
1895 : /* arranged in geometrically adjacent and consecutive */
1896 : /* sequence. */
1897 : /****************************************************************/
1898 11 : if( bGetSecondaryGeometry )
1899 0 : return NULL;
1900 : const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild;
1901 11 : int bFaceOrientation = TRUE;
1902 11 : OGRPolygon *poTS = new OGRPolygon();
1903 :
1904 : // collect directed faces
1905 33 : for( psChild = psNode->psChild;
1906 : psChild != NULL;
1907 : psChild = psChild->psNext )
1908 : {
1909 22 : if( psChild->eType == CXT_Element
1910 : && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") )
1911 : {
1912 20 : bFaceOrientation = GetElementOrientation(psChild);
1913 :
1914 : // collect next face (psChild->psChild)
1915 20 : psFaceChild = GetChildElement(psChild);
1916 40 : while( psFaceChild != NULL &&
1917 : !EQUAL(BareGMLElement(psFaceChild->pszValue),"Face") )
1918 0 : psFaceChild = psFaceChild->psNext;
1919 :
1920 20 : if( psFaceChild == NULL )
1921 0 : continue;
1922 :
1923 20 : OGRLinearRing *poFaceGeom = new OGRLinearRing();
1924 :
1925 : // collect directed edges of the face
1926 130 : for( psDirectedEdgeChild = psFaceChild->psChild;
1927 : psDirectedEdgeChild != NULL;
1928 : psDirectedEdgeChild = psDirectedEdgeChild->psNext )
1929 : {
1930 110 : if( psDirectedEdgeChild->eType == CXT_Element &&
1931 : EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") )
1932 : {
1933 : OGRGeometry *poEdgeGeom;
1934 :
1935 : poEdgeGeom = GML2OGRGeometry_XMLNode( psDirectedEdgeChild,
1936 : bGetSecondaryGeometryOption,
1937 : nRecLevel + 1,
1938 : TRUE,
1939 92 : bFaceOrientation );
1940 :
1941 184 : if( poEdgeGeom == NULL ||
1942 92 : wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString )
1943 : {
1944 : CPLError( CE_Failure, CPLE_AppDefined,
1945 0 : "Failed to get geometry in directedEdge" );
1946 0 : delete poEdgeGeom;
1947 0 : delete poFaceGeom;
1948 0 : delete poTS;
1949 0 : return NULL;
1950 : }
1951 :
1952 92 : if( !bFaceOrientation )
1953 : {
1954 15 : if( poFaceGeom->getNumPoints() > 0 )
1955 9 : ((OGRLinearRing *)poEdgeGeom)->addSubLineString( (OGRLineString *)poFaceGeom );
1956 15 : poFaceGeom->empty();
1957 : }
1958 92 : poFaceGeom->addSubLineString( (OGRLinearRing *)poEdgeGeom );
1959 92 : delete poEdgeGeom;
1960 : }
1961 : }
1962 :
1963 : /* if( poFaceGeom == NULL )
1964 : {
1965 : CPLError( CE_Failure, CPLE_AppDefined,
1966 : "Failed to get Face geometry in directedFace" );
1967 : delete poFaceGeom;
1968 : return NULL;
1969 : }*/
1970 :
1971 20 : poTS->addRingDirectly( poFaceGeom );
1972 : }
1973 : }
1974 :
1975 : /* if( poTS == NULL )
1976 : {
1977 : CPLError( CE_Failure, CPLE_AppDefined,
1978 : "Failed to get TopoSurface geometry" );
1979 : delete poTS;
1980 : return NULL;
1981 : }*/
1982 :
1983 11 : return poTS;
1984 : }
1985 :
1986 : /* -------------------------------------------------------------------- */
1987 : /* Surface */
1988 : /* -------------------------------------------------------------------- */
1989 386 : if( EQUAL(pszBaseGeometry,"Surface") )
1990 : {
1991 : const CPLXMLNode *psChild;
1992 357 : OGRGeometry *poResult = NULL;
1993 :
1994 : // Find outer ring.
1995 357 : psChild = FindBareXMLChild( psNode, "patches" );
1996 357 : if( psChild == NULL )
1997 335 : psChild = FindBareXMLChild( psNode, "polygonPatches" );
1998 357 : if( psChild == NULL )
1999 2 : psChild = FindBareXMLChild( psNode, "trianglePatches" );
2000 :
2001 357 : psChild = GetChildElement(psChild);
2002 357 : if( psChild == NULL )
2003 : {
2004 : /* <gml:Surface/> and <gml:Surface><gml:patches/></gml:Surface> are valid GML */
2005 3 : return new OGRPolygon();
2006 : }
2007 :
2008 709 : for( ; psChild != NULL; psChild = psChild->psNext )
2009 : {
2010 355 : if( psChild->eType == CXT_Element
2011 : && (EQUAL(BareGMLElement(psChild->pszValue),"PolygonPatch") ||
2012 : EQUAL(BareGMLElement(psChild->pszValue),"Triangle") ||
2013 : EQUAL(BareGMLElement(psChild->pszValue),"Rectangle")))
2014 : {
2015 : OGRPolygon *poPolygon = (OGRPolygon *)
2016 : GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
2017 354 : nRecLevel + 1 );
2018 354 : if( poPolygon == NULL )
2019 0 : return NULL;
2020 :
2021 354 : if( poResult == NULL )
2022 353 : poResult = poPolygon;
2023 1 : else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon )
2024 : {
2025 1 : OGRMultiPolygon *poMP = new OGRMultiPolygon();
2026 1 : poMP->addGeometryDirectly( poResult );
2027 1 : poMP->addGeometryDirectly( poPolygon );
2028 1 : poResult = poMP;
2029 : }
2030 : else
2031 : {
2032 0 : ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon );
2033 : }
2034 : }
2035 : }
2036 :
2037 354 : return poResult;
2038 : }
2039 :
2040 : /* -------------------------------------------------------------------- */
2041 : /* TriangulatedSurface */
2042 : /* -------------------------------------------------------------------- */
2043 29 : if( EQUAL(pszBaseGeometry,"TriangulatedSurface") ||
2044 : EQUAL(pszBaseGeometry,"Tin") )
2045 : {
2046 : const CPLXMLNode *psChild;
2047 1 : OGRGeometry *poResult = NULL;
2048 :
2049 : // Find trianglePatches
2050 1 : psChild = FindBareXMLChild( psNode, "trianglePatches" );
2051 1 : if (psChild == NULL)
2052 1 : psChild = FindBareXMLChild( psNode, "patches" );
2053 :
2054 1 : psChild = GetChildElement(psChild);
2055 1 : if( psChild == NULL )
2056 : {
2057 : CPLError( CE_Failure, CPLE_AppDefined,
2058 0 : "Missing <trianglePatches> for %s.", pszBaseGeometry );
2059 0 : return NULL;
2060 : }
2061 :
2062 2 : for( ; psChild != NULL; psChild = psChild->psNext )
2063 : {
2064 1 : if( psChild->eType == CXT_Element
2065 : && EQUAL(BareGMLElement(psChild->pszValue),"Triangle") )
2066 : {
2067 : OGRPolygon *poPolygon = (OGRPolygon *)
2068 : GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
2069 1 : nRecLevel + 1 );
2070 1 : if( poPolygon == NULL )
2071 0 : return NULL;
2072 :
2073 1 : if( poResult == NULL )
2074 1 : poResult = poPolygon;
2075 0 : else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon )
2076 : {
2077 0 : OGRMultiPolygon *poMP = new OGRMultiPolygon();
2078 0 : poMP->addGeometryDirectly( poResult );
2079 0 : poMP->addGeometryDirectly( poPolygon );
2080 0 : poResult = poMP;
2081 : }
2082 : else
2083 : {
2084 0 : ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon );
2085 : }
2086 : }
2087 : }
2088 :
2089 1 : return poResult;
2090 : }
2091 :
2092 : /* -------------------------------------------------------------------- */
2093 : /* Solid */
2094 : /* -------------------------------------------------------------------- */
2095 28 : if( EQUAL(pszBaseGeometry,"Solid") )
2096 : {
2097 : const CPLXMLNode *psChild;
2098 : OGRGeometry* poGeom;
2099 :
2100 : // Find exterior element
2101 6 : psChild = FindBareXMLChild( psNode, "exterior");
2102 :
2103 6 : psChild = GetChildElement(psChild);
2104 6 : if( psChild == NULL )
2105 : {
2106 : /* <gml:Solid/> and <gml:Solid><gml:exterior/></gml:Solid> are valid GML */
2107 3 : return new OGRPolygon();
2108 : }
2109 :
2110 : // Get the geometry inside <exterior>
2111 : poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
2112 3 : nRecLevel + 1 );
2113 3 : if( poGeom == NULL )
2114 : {
2115 1 : CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior element");
2116 1 : delete poGeom;
2117 1 : return NULL;
2118 : }
2119 :
2120 2 : psChild = FindBareXMLChild( psNode, "interior");
2121 2 : if( psChild != NULL )
2122 : {
2123 : static int bWarnedOnce = FALSE;
2124 1 : if (!bWarnedOnce)
2125 : {
2126 : CPLError( CE_Warning, CPLE_AppDefined,
2127 1 : "<interior> elements of <Solid> are ignored");
2128 1 : bWarnedOnce = TRUE;
2129 : }
2130 : }
2131 :
2132 2 : return poGeom;
2133 : }
2134 :
2135 : /* -------------------------------------------------------------------- */
2136 : /* OrientableSurface */
2137 : /* -------------------------------------------------------------------- */
2138 22 : if( EQUAL(pszBaseGeometry,"OrientableSurface") )
2139 : {
2140 : const CPLXMLNode *psChild;
2141 :
2142 : // Find baseSurface.
2143 5 : psChild = FindBareXMLChild( psNode, "baseSurface" );
2144 :
2145 5 : psChild = GetChildElement(psChild);
2146 5 : if( psChild == NULL )
2147 : {
2148 : CPLError( CE_Failure, CPLE_AppDefined,
2149 3 : "Missing <baseSurface> for OrientableSurface." );
2150 3 : return NULL;
2151 : }
2152 :
2153 : return GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption,
2154 2 : nRecLevel + 1 );
2155 : }
2156 :
2157 : /* -------------------------------------------------------------------- */
2158 : /* SimplePolygon, SimpleRectangle, SimpleTriangle */
2159 : /* (GML 3.3 compact encoding) */
2160 : /* -------------------------------------------------------------------- */
2161 17 : if( EQUAL(pszBaseGeometry,"SimplePolygon") ||
2162 : EQUAL(pszBaseGeometry,"SimpleRectangle") ||
2163 : EQUAL(pszBaseGeometry,"SimpleTriangle") )
2164 : {
2165 6 : OGRLinearRing *poRing = new OGRLinearRing();
2166 :
2167 6 : if( !ParseGMLCoordinates( psNode, poRing ) )
2168 : {
2169 2 : delete poRing;
2170 2 : return NULL;
2171 : }
2172 :
2173 4 : poRing->closeRings();
2174 :
2175 4 : OGRPolygon* poPolygon = new OGRPolygon();
2176 4 : poPolygon->addRingDirectly(poRing);
2177 4 : return poPolygon;
2178 : }
2179 :
2180 : /* -------------------------------------------------------------------- */
2181 : /* SimpleMultiPoint (GML 3.3 compact encoding) */
2182 : /* -------------------------------------------------------------------- */
2183 11 : if( EQUAL(pszBaseGeometry,"SimpleMultiPoint") )
2184 : {
2185 4 : OGRLineString *poLS = new OGRLineString();
2186 :
2187 4 : if( !ParseGMLCoordinates( psNode, poLS ) )
2188 : {
2189 2 : delete poLS;
2190 2 : return NULL;
2191 : }
2192 :
2193 2 : OGRMultiPoint* poMP = new OGRMultiPoint();
2194 2 : int nPoints = poLS->getNumPoints();
2195 8 : for(int i = 0; i < nPoints; i++)
2196 : {
2197 2 : OGRPoint* poPoint = new OGRPoint();
2198 2 : poLS->getPoint(i, poPoint);
2199 2 : poMP->addGeometryDirectly(poPoint);
2200 : }
2201 2 : delete poLS;
2202 2 : return poMP;
2203 : }
2204 :
2205 : CPLError( CE_Failure, CPLE_AppDefined,
2206 : "Unrecognised geometry type <%.500s>.",
2207 7 : pszBaseGeometry );
2208 :
2209 7 : return NULL;
2210 : }
2211 :
2212 : /************************************************************************/
2213 : /* OGR_G_CreateFromGMLTree() */
2214 : /************************************************************************/
2215 :
2216 342 : OGRGeometryH OGR_G_CreateFromGMLTree( const CPLXMLNode *psTree )
2217 :
2218 : {
2219 342 : return (OGRGeometryH) GML2OGRGeometry_XMLNode( psTree, -1 );
2220 : }
2221 :
2222 : /************************************************************************/
2223 : /* OGR_G_CreateFromGML() */
2224 : /************************************************************************/
2225 :
2226 : /**
2227 : * \brief Create geometry from GML.
2228 : *
2229 : * This method translates a fragment of GML containing only the geometry
2230 : * portion into a corresponding OGRGeometry. There are many limitations
2231 : * on the forms of GML geometries supported by this parser, but they are
2232 : * too numerous to list here.
2233 : *
2234 : * The following GML2 elements are parsed : Point, LineString, Polygon,
2235 : * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry.
2236 : *
2237 : * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface,
2238 : * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, CompositeCurve,
2239 : * LineStringSegment, Arc, Circle, CompositeSurface, OrientableSurface, Solid,
2240 : * Tin, TriangulatedSurface.
2241 : *
2242 : * Arc and Circle elements are stroked to linestring, by using a
2243 : * 4 degrees step, unless the user has overridden the value with the
2244 : * OGR_ARC_STEPSIZE configuration variable.
2245 : *
2246 : * The C++ method OGRGeometryFactory::createFromGML() is the same as this function.
2247 : *
2248 : * @param pszGML The GML fragment for the geometry.
2249 : *
2250 : * @return a geometry on succes, or NULL on error.
2251 : */
2252 :
2253 176 : OGRGeometryH OGR_G_CreateFromGML( const char *pszGML )
2254 :
2255 : {
2256 176 : if( pszGML == NULL || strlen(pszGML) == 0 )
2257 : {
2258 : CPLError( CE_Failure, CPLE_AppDefined,
2259 0 : "GML Geometry is empty in OGR_G_CreateFromGML()." );
2260 0 : return NULL;
2261 : }
2262 :
2263 : /* ------------------------------------------------------------ -------- */
2264 : /* Try to parse the XML snippet using the MiniXML API. If this */
2265 : /* fails, we assume the minixml api has already posted a CPL */
2266 : /* error, and just return NULL. */
2267 : /* -------------------------------------------------------------------- */
2268 176 : CPLXMLNode *psGML = CPLParseXMLString( pszGML );
2269 :
2270 176 : if( psGML == NULL )
2271 0 : return NULL;
2272 :
2273 : /* -------------------------------------------------------------------- */
2274 : /* Convert geometry recursively. */
2275 : /* -------------------------------------------------------------------- */
2276 : OGRGeometry *poGeometry;
2277 :
2278 : /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */
2279 176 : int bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO"));
2280 176 : poGeometry = GML2OGRGeometry_XMLNode( psGML, -1, 0, FALSE, TRUE, bFaceHoleNegative );
2281 :
2282 176 : CPLDestroyXMLNode( psGML );
2283 :
2284 176 : return (OGRGeometryH) poGeometry;
2285 : }
2286 :
2287 :
|