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