EVOLUTION-MANAGER
Edit File: gml2ogrgeometry.cpp
/****************************************************************************** * $Id: gml2ogrgeometry.cpp 27908 2014-10-26 13:06:58Z rouault $ * * Project: GML Reader * Purpose: Code to translate between GML and OGR geometry forms. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam * Copyright (c) 2009-2014, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ***************************************************************************** * * Independent Security Audit 2003/04/17 Andrey Kiselev: * Completed audit of this module. All functions may be used without buffer * overflows and stack corruptions with any kind of input data. * * Security Audit 2003/03/28 warmerda: * Completed security audit. I believe that this module may be safely used * to parse, arbitrary GML potentially provided by a hostile source without * compromising the system. * */ #include "cpl_minixml.h" #include "ogr_geometry.h" #include "ogr_api.h" #include "cpl_error.h" #include "cpl_string.h" #include <ctype.h> #include "ogr_p.h" #ifndef PI #define PI 3.14159265358979323846 #endif /************************************************************************/ /* GMLGetCoordTokenPos() */ /************************************************************************/ static const char* GMLGetCoordTokenPos(const char* pszStr, const char** ppszNextToken) { char ch; while(TRUE) { ch = *pszStr; if (ch == '\0') { *ppszNextToken = NULL; return NULL; } else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',')) break; pszStr ++; } const char* pszToken = pszStr; while((ch = *pszStr) != '\0') { if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',') { *ppszNextToken = pszStr; return pszToken; } pszStr ++; } *ppszNextToken = NULL; return pszToken; } /************************************************************************/ /* BareGMLElement() */ /* */ /* Returns the passed string with any namespace prefix */ /* stripped off. */ /************************************************************************/ static const char *BareGMLElement( const char *pszInput ) { const char *pszReturn; pszReturn = strchr( pszInput, ':' ); if( pszReturn == NULL ) pszReturn = pszInput; else pszReturn++; return pszReturn; } /************************************************************************/ /* FindBareXMLChild() */ /* */ /* Find a child node with the indicated "bare" name, that is */ /* after any namespace qualifiers have been stripped off. */ /************************************************************************/ static const CPLXMLNode *FindBareXMLChild( const CPLXMLNode *psParent, const char *pszBareName ) { const CPLXMLNode *psCandidate = psParent->psChild; while( psCandidate != NULL ) { if( psCandidate->eType == CXT_Element && EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName) ) return psCandidate; psCandidate = psCandidate->psNext; } return NULL; } /************************************************************************/ /* GetElementText() */ /************************************************************************/ static const char *GetElementText( const CPLXMLNode *psElement ) { if( psElement == NULL ) return NULL; const CPLXMLNode *psChild = psElement->psChild; while( psChild != NULL ) { if( psChild->eType == CXT_Text ) return psChild->pszValue; psChild = psChild->psNext; } return NULL; } /************************************************************************/ /* GetChildElement() */ /************************************************************************/ static const CPLXMLNode *GetChildElement( const CPLXMLNode *psElement ) { if( psElement == NULL ) return NULL; const CPLXMLNode *psChild = psElement->psChild; while( psChild != NULL ) { if( psChild->eType == CXT_Element ) return psChild; psChild = psChild->psNext; } return NULL; } /************************************************************************/ /* GetElementOrientation() */ /* Returns true for positive orientation. */ /************************************************************************/ int GetElementOrientation( const CPLXMLNode *psElement ) { if( psElement == NULL ) return TRUE; const CPLXMLNode *psChild = psElement->psChild; while( psChild != NULL ) { if( psChild->eType == CXT_Attribute && EQUAL(psChild->pszValue,"orientation") ) return EQUAL(psChild->psChild->pszValue,"+"); psChild = psChild->psNext; } return TRUE; } /************************************************************************/ /* AddPoint() */ /* */ /* Add a point to the passed geometry. */ /************************************************************************/ static int AddPoint( OGRGeometry *poGeometry, double dfX, double dfY, double dfZ, int nDimension ) { OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType()); if( eType == wkbPoint ) { OGRPoint *poPoint = (OGRPoint *) poGeometry; if( !poPoint->IsEmpty() ) { CPLError( CE_Failure, CPLE_AppDefined, "More than one coordinate for <Point> element."); return FALSE; } poPoint->setX( dfX ); poPoint->setY( dfY ); if( nDimension == 3 ) poPoint->setZ( dfZ ); return TRUE; } else if( eType == wkbLineString ) { if( nDimension == 3 ) ((OGRLineString *) poGeometry)->addPoint( dfX, dfY, dfZ ); else ((OGRLineString *) poGeometry)->addPoint( dfX, dfY ); return TRUE; } else { CPLAssert( FALSE ); return FALSE; } } /************************************************************************/ /* ParseGMLCoordinates() */ /************************************************************************/ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeometry ) { const CPLXMLNode *psCoordinates = FindBareXMLChild( psGeomNode, "coordinates" ); int iCoord = 0; /* -------------------------------------------------------------------- */ /* Handle <coordinates> case. */ /* Note that we don't do a strict validation, so we accept and */ /* sometimes generate output whereas we should just reject it. */ /* -------------------------------------------------------------------- */ if( psCoordinates != NULL ) { const char *pszCoordString = GetElementText( psCoordinates ); const char *pszDecimal = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "decimal", NULL); char chDecimal = '.'; if( pszDecimal != NULL ) { if( strlen(pszDecimal) != 1 || (pszDecimal[0] >= '0' && pszDecimal[0] <= '9') ) { CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for decimal attribute"); return FALSE; } chDecimal = pszDecimal[0]; } const char *pszCS = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "cs", NULL); char chCS = ','; if( pszCS != NULL ) { if( strlen(pszCS) != 1 || (pszCS[0] >= '0' && pszCS[0] <= '9') ) { CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for cs attribute"); return FALSE; } chCS = pszCS[0]; } const char *pszTS = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "ts", NULL); char chTS = ' '; if( pszTS != NULL ) { if( strlen(pszTS) != 1 || (pszTS[0] >= '0' && pszTS[0] <= '9') ) { CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for tes attribute"); return FALSE; } chTS = pszTS[0]; } if( pszCoordString == NULL ) { poGeometry->empty(); return TRUE; } while( *pszCoordString != '\0' ) { double dfX, dfY, dfZ = 0.0; int nDimension = 2; // parse out 2 or 3 tuple. if( chDecimal == '.' ) dfX = OGRFastAtof( pszCoordString ); else dfX = CPLAtofDelim( pszCoordString, chDecimal) ; while( *pszCoordString != '\0' && *pszCoordString != chCS && !isspace((unsigned char)*pszCoordString) ) pszCoordString++; if( *pszCoordString == '\0' ) { CPLError( CE_Failure, CPLE_AppDefined, "Corrupt <coordinates> value." ); return FALSE; } else if( chCS == ',' && pszCS == NULL && isspace((unsigned char)*pszCoordString) ) { /* In theory, the coordinates inside a coordinate tuple should be */ /* separated by a comma. However it has been found in the wild */ /* that the coordinates are in rare cases separated by a space, and the tuples by a comma */ /* See https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample */ /* or http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW */ chCS = ' '; chTS = ','; } pszCoordString++; if( chDecimal == '.' ) dfY = OGRFastAtof( pszCoordString ); else dfY = CPLAtofDelim( pszCoordString, chDecimal) ; while( *pszCoordString != '\0' && *pszCoordString != chCS && *pszCoordString != chTS && !isspace((unsigned char)*pszCoordString) ) pszCoordString++; if( *pszCoordString == chCS ) { pszCoordString++; if( chDecimal == '.' ) dfZ = OGRFastAtof( pszCoordString ); else dfZ = CPLAtofDelim( pszCoordString, chDecimal) ; nDimension = 3; while( *pszCoordString != '\0' && *pszCoordString != chCS && *pszCoordString != chTS && !isspace((unsigned char)*pszCoordString) ) pszCoordString++; } if( *pszCoordString == chTS ) { pszCoordString++; } while( isspace((unsigned char)*pszCoordString) ) pszCoordString++; if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) ) return FALSE; iCoord++; } return iCoord > 0; } /* -------------------------------------------------------------------- */ /* Is this a "pos"? GML 3 construct. */ /* Parse if it exist a series of pos elements (this would allow */ /* the correct parsing of gml3.1.1 geomtries such as linestring */ /* defined with pos elements. */ /* -------------------------------------------------------------------- */ const CPLXMLNode *psPos; int bHasFoundPosElement = FALSE; for( psPos = psGeomNode->psChild; psPos != NULL; psPos = psPos->psNext ) { if( psPos->eType != CXT_Element ) continue; const char* pszSubElement = BareGMLElement(psPos->pszValue); if( EQUAL(pszSubElement, "pointProperty") ) { const CPLXMLNode *psPointPropertyIter; for( psPointPropertyIter = psPos->psChild; psPointPropertyIter != NULL; psPointPropertyIter = psPointPropertyIter->psNext ) { if( psPointPropertyIter->eType != CXT_Element ) continue; if (EQUAL(BareGMLElement(psPointPropertyIter->pszValue),"Point") ) { OGRPoint oPoint; if( ParseGMLCoordinates( psPointPropertyIter, &oPoint ) ) { int bSuccess = AddPoint( poGeometry, oPoint.getX(), oPoint.getY(), oPoint.getZ(), oPoint.getCoordinateDimension() ); if (bSuccess) bHasFoundPosElement = TRUE; else return FALSE; } } } continue; } if( !EQUAL(pszSubElement,"pos") ) continue; const char* pszPos = GetElementText( psPos ); if (pszPos == NULL) { poGeometry->empty(); return TRUE; } const char* pszCur = pszPos; const char* pszX = (pszCur != NULL) ? GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; const char* pszY = (pszCur != NULL) ? GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; const char* pszZ = (pszCur != NULL) ? GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; if (pszY == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Did not get 2+ values in <gml:pos>%s</gml:pos> tuple.", pszPos ? pszPos : "" ); return FALSE; } double dfX = OGRFastAtof(pszX); double dfY = OGRFastAtof(pszY); double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0; int bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, (pszZ != NULL) ? 3 : 2 ); if (bSuccess) bHasFoundPosElement = TRUE; else return FALSE; } if (bHasFoundPosElement) return TRUE; /* -------------------------------------------------------------------- */ /* Is this a "posList"? GML 3 construct (SF profile). */ /* -------------------------------------------------------------------- */ const CPLXMLNode *psPosList = FindBareXMLChild( psGeomNode, "posList" ); if( psPosList != NULL ) { int bSuccess = FALSE; int nDimension = 2; /* Try to detect the presence of an srsDimension attribute */ /* This attribute is only availabe for gml3.1.1 but not */ /* available for gml3.1 SF*/ const char* pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psPosList, "srsDimension", NULL); /* If not found at the posList level, try on the enclosing element */ if (pszSRSDimension == NULL) pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psGeomNode, "srsDimension", NULL); if (pszSRSDimension != NULL) nDimension = atoi(pszSRSDimension); if (nDimension != 2 && nDimension != 3) { CPLError( CE_Failure, CPLE_AppDefined, "srsDimension = %d not supported", nDimension); return FALSE; } const char* pszPosList = GetElementText( psPosList ); if (pszPosList == NULL) { poGeometry->empty(); return TRUE; } const char* pszCur = pszPosList; while (TRUE) { const char* pszX = GMLGetCoordTokenPos(pszCur, &pszCur); if (pszX == NULL && bSuccess) break; const char* pszY = (pszCur != NULL) ? GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; const char* pszZ = (nDimension == 3 && pszCur != NULL) ? GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; if (pszY == NULL || (nDimension == 3 && pszZ == NULL)) { CPLError( CE_Failure, CPLE_AppDefined, "Did not get at least %d values or invalid number of \n" "set of coordinates <gml:posList>%s</gml:posList>", nDimension, pszPosList ? pszPosList : ""); return FALSE; } double dfX = OGRFastAtof(pszX); double dfY = OGRFastAtof(pszY); double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0; bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ); if (bSuccess == FALSE || pszCur == NULL) break; } return bSuccess; } /* -------------------------------------------------------------------- */ /* Handle form with a list of <coord> items each with an <X>, */ /* and <Y> element. */ /* -------------------------------------------------------------------- */ const CPLXMLNode *psCoordNode; for( psCoordNode = psGeomNode->psChild; psCoordNode != NULL; psCoordNode = psCoordNode->psNext ) { if( psCoordNode->eType != CXT_Element || !EQUAL(BareGMLElement(psCoordNode->pszValue),"coord") ) continue; const CPLXMLNode *psXNode, *psYNode, *psZNode; double dfX, dfY, dfZ = 0.0; int nDimension = 2; psXNode = FindBareXMLChild( psCoordNode, "X" ); psYNode = FindBareXMLChild( psCoordNode, "Y" ); psZNode = FindBareXMLChild( psCoordNode, "Z" ); if( psXNode == NULL || psYNode == NULL || GetElementText(psXNode) == NULL || GetElementText(psYNode) == NULL || (psZNode != NULL && GetElementText(psZNode) == NULL) ) { CPLError( CE_Failure, CPLE_AppDefined, "Corrupt <coord> element, missing <X> or <Y> element?" ); return FALSE; } dfX = OGRFastAtof( GetElementText(psXNode) ); dfY = OGRFastAtof( GetElementText(psYNode) ); if( psZNode != NULL && GetElementText(psZNode) != NULL ) { dfZ = OGRFastAtof( GetElementText(psZNode) ); nDimension = 3; } if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) ) return FALSE; iCoord++; } return iCoord > 0.0; } #ifdef HAVE_GEOS /************************************************************************/ /* GML2FaceExtRing() */ /* */ /* Identifies the "good" Polygon whithin the collection returned */ /* by GEOSPolygonize() */ /* short rationale: GEOSPolygonize() will possibily return a */ /* collection of many Polygons; only one is the "good" one, */ /* (including both exterior- and interior-rings) */ /* any other simply represents a single "hole", and should be */ /* consequently ignored at all. */ /************************************************************************/ static OGRPolygon *GML2FaceExtRing( OGRGeometry *poGeom ) { OGRPolygon *poPolygon = NULL; int bError = FALSE; OGRGeometryCollection *poColl = (OGRGeometryCollection *)poGeom; int iCount = poColl->getNumGeometries(); int iExterior = 0; int iInterior = 0; for( int ig = 0; ig < iCount; ig++) { /* a collection of Polygons is expected to be found */ OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig); if( poChild == NULL) { bError = TRUE; continue; } if( wkbFlatten( poChild->getGeometryType()) == wkbPolygon ) { OGRPolygon *poPg = (OGRPolygon *)poChild; if( poPg->getNumInteriorRings() > 0 ) iExterior++; else iInterior++; } else bError = TRUE; } if( bError == FALSE && iCount > 0 ) { if( iCount == 1 && iExterior == 0 && iInterior == 1) { /* there is a single Polygon within the collection */ OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(0 ); poPolygon = (OGRPolygon *)poPg->clone(); } else { if( iExterior == 1 && iInterior == iCount - 1 ) { /* searching the unique Polygon containing holes */ for ( int ig = 0; ig < iCount; ig++) { OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(ig); if( poPg->getNumInteriorRings() > 0 ) poPolygon = (OGRPolygon *)poPg->clone(); } } } } return poPolygon; } #endif /************************************************************************/ /* GML2OGRGeometry_XMLNode() */ /* */ /* Translates the passed XMLnode and it's children into an */ /* OGRGeometry. This is used recursively for geometry */ /* collections. */ /************************************************************************/ OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode, int bGetSecondaryGeometryOption, int nRecLevel, int bIgnoreGSG, int bOrientation, int bFaceHoleNegative ) { if( psNode != NULL && strcmp(psNode->pszValue, "?xml") == 0 ) psNode = psNode->psNext; while( psNode != NULL && psNode->eType == CXT_Comment ) psNode = psNode->psNext; if( psNode == NULL ) return NULL; const char *pszBaseGeometry = BareGMLElement( psNode->pszValue ); if (bGetSecondaryGeometryOption < 0) bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO")); int bGetSecondaryGeometry = bIgnoreGSG ? FALSE : bGetSecondaryGeometryOption; /* Arbitrary value, but certainly large enough for reasonable usages ! */ if( nRecLevel == 32 ) { CPLError( CE_Failure, CPLE_AppDefined, "Too many recursion levels (%d) while parsing GML geometry.", nRecLevel ); return NULL; } if( bGetSecondaryGeometry ) if( !( EQUAL(pszBaseGeometry,"directedEdge") || EQUAL(pszBaseGeometry,"TopoCurve") ) ) return NULL; /* -------------------------------------------------------------------- */ /* Polygon / PolygonPatch / Triangle / Rectangle */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Polygon") || EQUAL(pszBaseGeometry,"PolygonPatch") || EQUAL(pszBaseGeometry,"Triangle") || EQUAL(pszBaseGeometry,"Rectangle")) { const CPLXMLNode *psChild; OGRPolygon *poPolygon = new OGRPolygon(); OGRLinearRing *poRing; // Find outer ring. psChild = FindBareXMLChild( psNode, "outerBoundaryIs" ); if (psChild == NULL) psChild = FindBareXMLChild( psNode, "exterior"); psChild = GetChildElement(psChild); if( psChild == NULL ) { /* <gml:Polygon/> is invalid GML2, but valid GML3, so be tolerant */ return poPolygon; } // Translate outer ring and add to polygon. poRing = (OGRLinearRing *) GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); if( poRing == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior ring"); delete poPolygon; return NULL; } if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) { CPLError( CE_Failure, CPLE_AppDefined, "%s: Got %.500s geometry as outerBoundaryIs instead of LINEARRING.", pszBaseGeometry, poRing->getGeometryName() ); delete poPolygon; delete poRing; return NULL; } poPolygon->addRingDirectly( poRing ); // Find all inner rings for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && (EQUAL(BareGMLElement(psChild->pszValue),"innerBoundaryIs") || EQUAL(BareGMLElement(psChild->pszValue),"interior"))) { const CPLXMLNode* psInteriorChild = GetChildElement(psChild); if (psInteriorChild != NULL) poRing = (OGRLinearRing *) GML2OGRGeometry_XMLNode( psInteriorChild, bGetSecondaryGeometryOption, nRecLevel + 1); else poRing = NULL; if (poRing == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring"); delete poPolygon; return NULL; } if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) { CPLError( CE_Failure, CPLE_AppDefined, "%s: Got %.500s geometry as innerBoundaryIs instead of LINEARRING.", pszBaseGeometry, poRing->getGeometryName() ); delete poPolygon; delete poRing; return NULL; } poPolygon->addRingDirectly( poRing ); } } return poPolygon; } /* -------------------------------------------------------------------- */ /* LinearRing */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"LinearRing") ) { OGRLinearRing *poLinearRing = new OGRLinearRing(); if( !ParseGMLCoordinates( psNode, poLinearRing ) ) { delete poLinearRing; return NULL; } return poLinearRing; } /* -------------------------------------------------------------------- */ /* Ring GML3 */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Ring") ) { OGRLinearRing *poLinearRing = new OGRLinearRing(); const CPLXMLNode *psChild; for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) { const CPLXMLNode* psCurveChild = GetChildElement(psChild); OGRGeometry* poGeom; if (psCurveChild != NULL) poGeom = GML2OGRGeometry_XMLNode( psCurveChild, bGetSecondaryGeometryOption, nRecLevel + 1); else poGeom = NULL; // try to join multiline string to one linestring if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) { poGeom = OGRGeometryFactory::forceToLineString( poGeom, false ); } if( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) { delete poGeom; delete poLinearRing; return NULL; } OGRLineString *poLS = (OGRLineString *) poGeom; if( poLS->getNumPoints() < 2 ) { // skip it } else if( poLinearRing->getNumPoints() > 0 && fabs(poLinearRing->getX(poLinearRing->getNumPoints()-1) - poLS->getX(0)) < 1e-14 && fabs(poLinearRing->getY(poLinearRing->getNumPoints()-1) - poLS->getY(0)) < 1e-14 && fabs(poLinearRing->getZ(poLinearRing->getNumPoints()-1) - poLS->getZ(0)) < 1e-14 ) { // Skip the first point of the new linestring to avoid // invalidate duplicate points poLinearRing->addSubLineString( poLS, 1 ); } else { // Add the whole new line string poLinearRing->addSubLineString( poLS ); } delete poLS; } } return poLinearRing; } /* -------------------------------------------------------------------- */ /* LineString */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"LineString") || EQUAL(pszBaseGeometry,"LineStringSegment") || EQUAL(pszBaseGeometry,"GeodesicString") ) { OGRLineString *poLine = new OGRLineString(); if( !ParseGMLCoordinates( psNode, poLine ) ) { delete poLine; return NULL; } return poLine; } /* -------------------------------------------------------------------- */ /* Arc/Circle : we approximate them by linear segments */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Arc") || EQUAL(pszBaseGeometry,"Circle") ) { OGRLineString *poLine = new OGRLineString(); if( !ParseGMLCoordinates( psNode, poLine ) || poLine->getNumPoints() != 3 ) { delete poLine; return NULL; } double x0 = poLine->getX(0); double y0 = poLine->getY(0); double x1 = poLine->getX(1); double y1 = poLine->getY(1); double x2 = poLine->getX(2); double y2 = poLine->getY(2); double dx01 = x1 - x0; double dy01 = y1 - y0; double dx12 = x2 - x1; double dy12 = y2 - y1; double c01 = dx01 * (x0 + x1) / 2 + dy01 * (y0 + y1) / 2; double c12 = dx12 * (x1 + x2) / 2 + dy12 * (y1 + y2) / 2; double det = dx01 * dy12 - dx12 * dy01; if (det == 0) { return poLine; } double cx = (c01 * dy12 - c12 * dy01) / det; double cy = (- c01 * dx12 + c12 * dx01) / det; double alpha0 = atan2(y0 - cy, x0 - cx); double alpha1 = atan2(y1 - cy, x1 - cx); double alpha2 = atan2(y2 - cy, x2 - cx); double alpha3; double R = sqrt((x0 - cx) * (x0 - cx) + (y0 - cy) * (y0 - cy)); /* if det is negative, the orientation if clockwise */ if (det < 0) { if (alpha1 > alpha0) alpha1 -= 2 * PI; if (alpha2 > alpha1) alpha2 -= 2 * PI; alpha3 = alpha0 - 2 * PI; } else { if (alpha1 < alpha0) alpha1 += 2 * PI; if (alpha2 < alpha1) alpha2 += 2 * PI; alpha3 = alpha0 + 2 * PI; } CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2 && alpha2 <= alpha3) || (alpha0 >= alpha1 && alpha1 >= alpha2 && alpha2 >= alpha3)); int nSign = (det >= 0) ? 1 : -1; double alpha, dfRemainder; double dfStep = atof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")) / 180 * PI; // make sure the segments are not too short double dfMinStepLength = atof( CPLGetConfigOption("OGR_ARC_MINLENGTH","0") ); if ( dfMinStepLength > 0.0 && dfStep * R < dfMinStepLength ) { CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with segment length %lf at radius %lf; min segment length is %lf)", dfMinStepLength * 180.0 / PI / R, dfStep * 180.0 / PI, dfStep * R, R, dfMinStepLength ); dfStep = dfMinStepLength / R; } if (dfStep < 4. / 180 * PI) { CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with length %lf at radius %lf).", 4. / 180 * PI, dfStep * 180.0 / PI, dfStep * R, R ); dfStep = 4. / 180 * PI; } poLine->setNumPoints(0); dfStep *= nSign; dfRemainder = fmod(alpha1 - alpha0, dfStep) / 2.0; poLine->addPoint(x0, y0); for(alpha = alpha0 + dfStep + dfRemainder; (alpha + dfRemainder - alpha1) * nSign < 0; alpha += dfStep) { poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); } poLine->addPoint(x1, y1); dfRemainder = fmod(alpha2 - alpha1, dfStep) / 2.0; for(alpha = alpha1 + dfStep + dfRemainder; (alpha + dfRemainder - alpha2) * nSign < 0; alpha += dfStep) { poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); } if (EQUAL(pszBaseGeometry,"Circle")) { for(alpha = alpha2; (alpha - alpha3) * nSign < 0; alpha += dfStep) { poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); } poLine->addPoint(x0, y0); } else { poLine->addPoint(x2, y2); } return poLine; } /* -------------------------------------------------------------------- */ /* PointType */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"PointType") || EQUAL(pszBaseGeometry,"Point") || EQUAL(pszBaseGeometry,"ConnectionPoint") ) { OGRPoint *poPoint = new OGRPoint(); if( !ParseGMLCoordinates( psNode, poPoint ) ) { delete poPoint; return NULL; } return poPoint; } /* -------------------------------------------------------------------- */ /* Box */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"BoxType") || EQUAL(pszBaseGeometry,"Box") ) { OGRLineString oPoints; if( !ParseGMLCoordinates( psNode, &oPoints ) ) return NULL; if( oPoints.getNumPoints() < 2 ) return NULL; OGRLinearRing *poBoxRing = new OGRLinearRing(); OGRPolygon *poBoxPoly = new OGRPolygon(); poBoxRing->setNumPoints( 5 ); poBoxRing->setPoint( 0, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) ); poBoxRing->setPoint( 1, oPoints.getX(1), oPoints.getY(0), oPoints.getZ(0) ); poBoxRing->setPoint( 2, oPoints.getX(1), oPoints.getY(1), oPoints.getZ(1) ); poBoxRing->setPoint( 3, oPoints.getX(0), oPoints.getY(1), oPoints.getZ(0) ); poBoxRing->setPoint( 4, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) ); poBoxPoly->addRingDirectly( poBoxRing ); return poBoxPoly; } /* -------------------------------------------------------------------- */ /* Envelope */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Envelope") ) { const CPLXMLNode* psLowerCorner = FindBareXMLChild( psNode, "lowerCorner"); const CPLXMLNode* psUpperCorner = FindBareXMLChild( psNode, "upperCorner"); if( psLowerCorner == NULL || psUpperCorner == NULL ) return NULL; const char* pszLowerCorner = GetElementText(psLowerCorner); const char* pszUpperCorner = GetElementText(psUpperCorner); if( pszLowerCorner == NULL || pszUpperCorner == NULL ) return NULL; char** papszLowerCorner = CSLTokenizeString(pszLowerCorner); char** papszUpperCorner = CSLTokenizeString(pszUpperCorner); int nTokenCountLC = CSLCount(papszLowerCorner); int nTokenCountUC = CSLCount(papszUpperCorner); if( nTokenCountLC < 2 || nTokenCountUC < 2 ) { CSLDestroy(papszLowerCorner); CSLDestroy(papszUpperCorner); return NULL; } double dfLLX = CPLAtof(papszLowerCorner[0]); double dfLLY = CPLAtof(papszLowerCorner[1]); double dfURX = CPLAtof(papszUpperCorner[0]); double dfURY = CPLAtof(papszUpperCorner[1]); CSLDestroy(papszLowerCorner); CSLDestroy(papszUpperCorner); OGRLinearRing *poEnvelopeRing = new OGRLinearRing(); OGRPolygon *poPoly = new OGRPolygon(); poEnvelopeRing->setNumPoints( 5 ); poEnvelopeRing->setPoint(0, dfLLX, dfLLY); poEnvelopeRing->setPoint(1, dfURX, dfLLY); poEnvelopeRing->setPoint(2, dfURX, dfURY); poEnvelopeRing->setPoint(3, dfLLX, dfURY); poEnvelopeRing->setPoint(4, dfLLX, dfLLY); poPoly->addRingDirectly(poEnvelopeRing ); return poPoly; } /* ------------------------const CPLXMLNode *psChild;-------------------------------------------- */ /* MultiPolygon / MultiSurface / CompositeSurface */ /* */ /* For CompositeSurface, this is a very rough approximation to deal with*/ /* it as a MultiPolygon, because it can several faces of a 3D volume... */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"MultiPolygon") || EQUAL(pszBaseGeometry,"MultiSurface") || EQUAL(pszBaseGeometry,"CompositeSurface") ) { const CPLXMLNode *psChild; OGRMultiPolygon *poMPoly = new OGRMultiPolygon(); int bReconstructTopology = FALSE; // Iterate over children for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && (EQUAL(BareGMLElement(psChild->pszValue),"polygonMember") || EQUAL(BareGMLElement(psChild->pszValue),"surfaceMember")) ) { const CPLXMLNode* psSurfaceChild = GetChildElement(psChild); OGRPolygon *poPolygon; if (psSurfaceChild != NULL) { /* Cf #5421 where there are PolygonPatch with only inner rings */ const CPLXMLNode* psPolygonPatch = GetChildElement(GetChildElement(psSurfaceChild)); if( psPolygonPatch != NULL && psPolygonPatch->eType == CXT_Element && EQUAL(BareGMLElement(psPolygonPatch->pszValue),"PolygonPatch") && GetChildElement(psPolygonPatch) != NULL && EQUAL(BareGMLElement(GetChildElement(psPolygonPatch)->pszValue),"interior") ) { // Find all inner rings for( const CPLXMLNode* psChild2 = psPolygonPatch->psChild; psChild2 != NULL; psChild2 = psChild2->psNext ) { if( psChild2->eType == CXT_Element && (EQUAL(BareGMLElement(psChild2->pszValue),"interior"))) { const CPLXMLNode* psInteriorChild = GetChildElement(psChild2); OGRLinearRing* poRing; if (psInteriorChild != NULL) poRing = (OGRLinearRing *) GML2OGRGeometry_XMLNode( psInteriorChild, bGetSecondaryGeometryOption, nRecLevel + 1); else poRing = NULL; if (poRing == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring"); delete poMPoly; return NULL; } if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) { CPLError( CE_Failure, CPLE_AppDefined, "%s: Got %.500s geometry as innerBoundaryIs instead of LINEARRING.", pszBaseGeometry, poRing->getGeometryName() ); delete poRing; delete poMPoly; return NULL; } bReconstructTopology = TRUE; poPolygon = new OGRPolygon(); poPolygon->addRingDirectly( poRing ); poMPoly->addGeometryDirectly( poPolygon ); } } } else { poPolygon = (OGRPolygon *) GML2OGRGeometry_XMLNode( psSurfaceChild, bGetSecondaryGeometryOption, nRecLevel + 1); if( poPolygon == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", BareGMLElement(psChild->pszValue)); delete poMPoly; return NULL; } if( !EQUAL(poPolygon->getGeometryName(),"POLYGON") ) { CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as polygonMember instead of MULTIPOLYGON.", poPolygon->getGeometryName() ); delete poPolygon; delete poMPoly; return NULL; } poMPoly->addGeometryDirectly( poPolygon ); } } } else if (psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"surfaceMembers") ) { const CPLXMLNode *psChild2; for( psChild2 = psChild->psChild; psChild2 != NULL; psChild2 = psChild2->psNext ) { if( psChild2->eType == CXT_Element && (EQUAL(BareGMLElement(psChild2->pszValue),"Surface") || EQUAL(BareGMLElement(psChild2->pszValue),"Polygon") || EQUAL(BareGMLElement(psChild2->pszValue),"PolygonPatch") || EQUAL(BareGMLElement(psChild2->pszValue),"CompositeSurface")) ) { OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption, nRecLevel + 1); if (poGeom == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", BareGMLElement(psChild2->pszValue)); delete poMPoly; return NULL; } if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) { poMPoly->addGeometryDirectly( (OGRPolygon*) poGeom ); } else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) { OGRMultiPolygon* poMPoly2 = (OGRMultiPolygon*) poGeom; int i; for(i=0;i<poMPoly2->getNumGeometries();i++) { poMPoly->addGeometry(poMPoly2->getGeometryRef(i)); } delete poGeom; } else { CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as polygonMember instead of POLYGON/MULTIPOLYGON.", poGeom->getGeometryName() ); delete poGeom; delete poMPoly; return NULL; } } } } } if( bReconstructTopology ) { int nPolygonCount = poMPoly->getNumGeometries(); OGRGeometry** papoPolygons = new OGRGeometry*[ nPolygonCount ]; for(int i=0;i<nPolygonCount;i++) { papoPolygons[i] = poMPoly->getGeometryRef(0); poMPoly->removeGeometry(0, FALSE); } delete poMPoly; int bResultValidGeometry = FALSE; OGRGeometry* poRet = OGRGeometryFactory::organizePolygons( papoPolygons, nPolygonCount, &bResultValidGeometry ); delete[] papoPolygons; return poRet; } else return poMPoly; } /* -------------------------------------------------------------------- */ /* MultiPoint */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"MultiPoint") ) { const CPLXMLNode *psChild; OGRMultiPoint *poMP = new OGRMultiPoint(); // collect points. for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"pointMember") ) { const CPLXMLNode* psPointChild = GetChildElement(psChild); OGRPoint *poPoint; if (psPointChild != NULL) { poPoint = (OGRPoint *) GML2OGRGeometry_XMLNode( psPointChild, bGetSecondaryGeometryOption, nRecLevel + 1); if( poPoint == NULL || wkbFlatten(poPoint->getGeometryType()) != wkbPoint ) { CPLError( CE_Failure, CPLE_AppDefined, "MultiPoint: Got %.500s geometry as pointMember instead of POINT", poPoint ? poPoint->getGeometryName() : "NULL" ); delete poPoint; delete poMP; return NULL; } poMP->addGeometryDirectly( poPoint ); } } else if (psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"pointMembers") ) { const CPLXMLNode *psChild2; for( psChild2 = psChild->psChild; psChild2 != NULL; psChild2 = psChild2->psNext ) { if( psChild2->eType == CXT_Element && (EQUAL(BareGMLElement(psChild2->pszValue),"Point")) ) { OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption, nRecLevel + 1); if (poGeom == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", BareGMLElement(psChild2->pszValue)); delete poMP; return NULL; } if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint) { poMP->addGeometryDirectly( (OGRPoint *)poGeom ); } else { CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as pointMember instead of POINT.", poGeom->getGeometryName() ); delete poGeom; delete poMP; return NULL; } } } } } return poMP; } /* -------------------------------------------------------------------- */ /* MultiLineString */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"MultiLineString") ) { const CPLXMLNode *psChild; OGRMultiLineString *poMLS = new OGRMultiLineString(); // collect lines for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"lineStringMember") ) { const CPLXMLNode* psLineStringChild = GetChildElement(psChild); OGRGeometry *poGeom; if (psLineStringChild != NULL) poGeom = GML2OGRGeometry_XMLNode( psLineStringChild, bGetSecondaryGeometryOption, nRecLevel + 1); else poGeom = NULL; if( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, "MultiLineString: Got %.500s geometry as Member instead of LINESTRING.", poGeom ? poGeom->getGeometryName() : "NULL" ); delete poGeom; delete poMLS; return NULL; } poMLS->addGeometryDirectly( poGeom ); } } return poMLS; } /* -------------------------------------------------------------------- */ /* MultiCurve / CompositeCurve */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"MultiCurve") || EQUAL(pszBaseGeometry,"CompositeCurve") ) { const CPLXMLNode *psChild, *psCurve; OGRMultiLineString *poMLS = new OGRMultiLineString(); // collect curveMembers for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) { OGRGeometry *poGeom; // There can be only one curve under a curveMember. // Currently "Curve" and "LineString" are handled. psCurve = FindBareXMLChild( psChild, "Curve" ); if( psCurve == NULL ) psCurve = FindBareXMLChild( psChild, "LineString" ); if( psCurve == NULL ) { if( GetChildElement(psChild) != NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get curve element in curveMember" ); delete poMLS; return NULL; } } else { poGeom = GML2OGRGeometry_XMLNode( psCurve, bGetSecondaryGeometryOption, nRecLevel + 1); if( poGeom == NULL || ( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) ) { CPLError( CE_Failure, CPLE_AppDefined, "MultiCurve: Got %.500s geometry as Member instead of LINESTRING.", poGeom ? poGeom->getGeometryName() : "NULL" ); if( poGeom != NULL ) delete poGeom; delete poMLS; return NULL; } poMLS->addGeometryDirectly( (OGRLineString *)poGeom ); } } else if (psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"curveMembers") ) { const CPLXMLNode *psChild2; for( psChild2 = psChild->psChild; psChild2 != NULL; psChild2 = psChild2->psNext ) { if( psChild2->eType == CXT_Element && (EQUAL(BareGMLElement(psChild2->pszValue),"LineString")) ) { OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption, nRecLevel + 1); if (poGeom == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", BareGMLElement(psChild2->pszValue)); delete poMLS; return NULL; } if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString) { poMLS->addGeometryDirectly( (OGRLineString *)poGeom ); } else { CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as curveMember instead of LINESTRING.", poGeom->getGeometryName() ); delete poGeom; delete poMLS; return NULL; } } } } } return poMLS; } /* -------------------------------------------------------------------- */ /* Curve */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Curve") ) { const CPLXMLNode *psChild; psChild = FindBareXMLChild( psNode, "segments"); if( psChild == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "GML3 Curve geometry lacks segments element." ); return NULL; } OGRGeometry *poGeom; poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1); if( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, "Curve: Got %.500s geometry as Member instead of segments.", poGeom ? poGeom->getGeometryName() : "NULL" ); if( poGeom != NULL ) delete poGeom; return NULL; } return poGeom; } /* -------------------------------------------------------------------- */ /* segments */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"segments") ) { const CPLXMLNode *psChild; OGRLineString *poLS = new OGRLineString(); for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && (EQUAL(BareGMLElement(psChild->pszValue),"LineStringSegment") || EQUAL(BareGMLElement(psChild->pszValue),"GeodesicString") || EQUAL(BareGMLElement(psChild->pszValue),"Arc") || EQUAL(BareGMLElement(psChild->pszValue),"Circle")) ) { OGRGeometry *poGeom; poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1); if( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, "segments: Got %.500s geometry as Member instead of LINESTRING.", poGeom ? poGeom->getGeometryName() : "NULL" ); delete poGeom; delete poLS; return NULL; } OGRLineString *poAddLS = (OGRLineString *)poGeom; if( poAddLS != NULL && poAddLS->getNumPoints() >= 2 ) { if( poLS->getNumPoints() > 0 && fabs(poLS->getX(poLS->getNumPoints()-1) - poAddLS->getX(0)) < 1e-14 && fabs(poLS->getY(poLS->getNumPoints()-1) - poAddLS->getY(0)) < 1e-14 && fabs(poLS->getZ(poLS->getNumPoints()-1) - poAddLS->getZ(0)) < 1e-14) { // Skip the first point of the new linestring to avoid // invalidate duplicate points (#4451) poLS->addSubLineString( poAddLS, 1 ); } else { // Add the whole new line string poLS->addSubLineString( poAddLS ); } } delete poGeom; } } return poLS; } /* -------------------------------------------------------------------- */ /* MultiGeometry */ /* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is */ /* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */ /* tolerant with the non compliant files we produced... */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"MultiGeometry") || EQUAL(pszBaseGeometry,"GeometryCollection") ) { const CPLXMLNode *psChild; OGRGeometryCollection *poGC = new OGRGeometryCollection(); // collect geoms for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"geometryMember") ) { const CPLXMLNode* psGeometryChild = GetChildElement(psChild); OGRGeometry *poGeom; if (psGeometryChild != NULL) { poGeom = GML2OGRGeometry_XMLNode( psGeometryChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); if( poGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "GeometryCollection: Failed to get geometry in geometryMember" ); delete poGeom; delete poGC; return NULL; } poGC->addGeometryDirectly( poGeom ); } } } return poGC; } /* -------------------------------------------------------------------- */ /* Directed Edge */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"directedEdge") ) { const CPLXMLNode *psEdge, *psdirectedNode, *psNodeElement, *pspointProperty, *psPoint, *psCurveProperty, *psCurve; int bEdgeOrientation = TRUE, bNodeOrientation = TRUE; OGRGeometry *poGeom; OGRLineString *poLineString; OGRPoint *poPositiveNode = NULL, *poNegativeNode = NULL; OGRMultiPoint *poMP; bEdgeOrientation = GetElementOrientation(psNode); //collect edge psEdge = FindBareXMLChild(psNode,"Edge"); if( psEdge == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get Edge element in directedEdge" ); return NULL; } if( bGetSecondaryGeometry ) { psdirectedNode = FindBareXMLChild(psEdge,"directedNode"); if( psdirectedNode == NULL ) goto nonode; bNodeOrientation = GetElementOrientation( psdirectedNode ); psNodeElement = FindBareXMLChild(psdirectedNode,"Node"); if( psNodeElement == NULL ) goto nonode; pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty"); if( pspointProperty == NULL ) pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty"); if( pspointProperty == NULL ) goto nonode; psPoint = FindBareXMLChild(pspointProperty,"Point"); if( psPoint == NULL ) psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint"); if( psPoint == NULL ) goto nonode; poGeom = GML2OGRGeometry_XMLNode( psPoint, bGetSecondaryGeometryOption, nRecLevel + 1, TRUE ); if( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) { /* CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as Member instead of POINT.", poGeom ? poGeom->getGeometryName() : "NULL" );*/ if( poGeom != NULL) delete poGeom; goto nonode; } if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation ) poPositiveNode = (OGRPoint *)poGeom; else poNegativeNode = (OGRPoint *)poGeom; // look for the other node psdirectedNode = psdirectedNode->psNext; while( psdirectedNode != NULL && !EQUAL( psdirectedNode->pszValue, "directedNode" ) ) psdirectedNode = psdirectedNode->psNext; if( psdirectedNode == NULL ) goto nonode; if( GetElementOrientation( psdirectedNode ) == bNodeOrientation ) goto nonode; psNodeElement = FindBareXMLChild(psEdge,"Node"); if( psNodeElement == NULL ) goto nonode; pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty"); if( pspointProperty == NULL ) pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty"); if( pspointProperty == NULL ) goto nonode; psPoint = FindBareXMLChild(pspointProperty,"Point"); if( psPoint == NULL ) psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint"); if( psPoint == NULL ) goto nonode; poGeom = GML2OGRGeometry_XMLNode( psPoint, bGetSecondaryGeometryOption, nRecLevel + 1, TRUE ); if( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) { /* CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as Member instead of POINT.", poGeom ? poGeom->getGeometryName() : "NULL" );*/ if( poGeom != NULL) delete poGeom; goto nonode; } if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation ) poNegativeNode = (OGRPoint *)poGeom; else poPositiveNode = (OGRPoint *)poGeom; poMP = new OGRMultiPoint(); poMP->addGeometryDirectly( poNegativeNode ); poMP->addGeometryDirectly( poPositiveNode ); return poMP; nonode:; } // collect curveproperty psCurveProperty = FindBareXMLChild(psEdge,"curveProperty"); if( psCurveProperty == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "directedEdge: Failed to get curveProperty in Edge" ); return NULL; } psCurve = FindBareXMLChild(psCurveProperty,"LineString"); if( psCurve == NULL ) psCurve = FindBareXMLChild(psCurveProperty,"Curve"); if( psCurve == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "directedEdge: Failed to get LineString or Curve tag in curveProperty" ); return NULL; } poLineString = (OGRLineString *)GML2OGRGeometry_XMLNode( psCurve, bGetSecondaryGeometryOption, nRecLevel + 1, TRUE ); if( poLineString == NULL || wkbFlatten(poLineString->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as Member instead of LINESTRING.", poLineString ? poLineString->getGeometryName() : "NULL" ); if( poLineString != NULL ) delete poLineString; return NULL; } if( bGetSecondaryGeometry ) { // choose a point based on the orientation poNegativeNode = new OGRPoint(); poPositiveNode = new OGRPoint(); if( bEdgeOrientation == bOrientation ) { poLineString->StartPoint( poNegativeNode ); poLineString->EndPoint( poPositiveNode ); } else { poLineString->StartPoint( poPositiveNode ); poLineString->EndPoint( poNegativeNode ); } delete poLineString; poMP = new OGRMultiPoint(); poMP->addGeometryDirectly( poNegativeNode ); poMP->addGeometryDirectly( poPositiveNode ); return poMP; } // correct orientation of the line string if( bEdgeOrientation != bOrientation ) { int iStartCoord = 0, iEndCoord = poLineString->getNumPoints() - 1; OGRPoint *poTempStartPoint = new OGRPoint(); OGRPoint *poTempEndPoint = new OGRPoint(); while( iStartCoord < iEndCoord ) { poLineString->getPoint( iStartCoord, poTempStartPoint ); poLineString->getPoint( iEndCoord, poTempEndPoint ); poLineString->setPoint( iStartCoord, poTempEndPoint ); poLineString->setPoint( iEndCoord, poTempStartPoint ); iStartCoord++; iEndCoord--; } delete poTempStartPoint; delete poTempEndPoint; } return poLineString; } /* -------------------------------------------------------------------- */ /* TopoCurve */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"TopoCurve") ) { const CPLXMLNode *psChild; OGRMultiLineString *poMLS = NULL; OGRMultiPoint *poMP = NULL; if( bGetSecondaryGeometry ) poMP = new OGRMultiPoint(); else poMLS = new OGRMultiLineString(); // collect directedEdges for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"directedEdge")) { OGRGeometry *poGeom; poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); if( poGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get geometry in directedEdge" ); delete poGeom; if( bGetSecondaryGeometry ) delete poMP; else delete poMLS; return NULL; } //Add the two points corresponding to the two nodes to poMP if( bGetSecondaryGeometry && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) { //TODO: TopoCurve geometries with more than one // directedEdge elements were not tested. if( poMP->getNumGeometries() <= 0 || !(poMP->getGeometryRef( poMP->getNumGeometries() - 1 )->Equals(((OGRMultiPoint *)poGeom)->getGeometryRef( 0 ) ) )) { poMP->addGeometry( ( (OGRMultiPoint *)poGeom )->getGeometryRef( 0 ) ); } poMP->addGeometry( ( (OGRMultiPoint *)poGeom )->getGeometryRef( 1 ) ); delete poGeom; } else if( !bGetSecondaryGeometry && wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) { poMLS->addGeometryDirectly( poGeom ); } else { CPLError( CE_Failure, CPLE_AppDefined, "Got %.500s geometry as Member instead of %s.", poGeom ? poGeom->getGeometryName() : "NULL", bGetSecondaryGeometry?"MULTIPOINT":"LINESTRING"); delete poGeom; if( bGetSecondaryGeometry ) delete poMP; else delete poMLS; return NULL; } } } if( bGetSecondaryGeometry ) return poMP; else return poMLS; } /* -------------------------------------------------------------------- */ /* TopoSurface */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"TopoSurface") ) { /****************************************************************/ /* applying the FaceHoleNegative = FALSE rules */ /* */ /* - each <TopoSurface> is expected to represent a MultiPolygon */ /* - each <Face> is expected to represent a distinct Polygon, */ /* this including any possible Interior Ring (holes); */ /* orientation="+/-" plays no role at all to identify "holes" */ /* - each <Edge> within a <Face> may indifferently represent */ /* an element of the Exterior or Interior Boundary; relative */ /* order of <Egdes> is absolutely irrelevant. */ /****************************************************************/ /* Contributor: Alessandro Furieri, a.furieri@lqt.it */ /* Developed for Faunalia (http://www.faunalia.it) */ /* with funding from Regione Toscana - */ /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE */ /****************************************************************/ if(bFaceHoleNegative != TRUE) { if( bGetSecondaryGeometry ) return NULL; #ifndef HAVE_GEOS static int bWarningAlreadyEmitted = FALSE; if (!bWarningAlreadyEmitted) { CPLError(CE_Failure, CPLE_AppDefined, "Interpreating that GML TopoSurface geometry requires GDAL to be built with GEOS support.\n" "As a workaround, you can try defining the GML_FACE_HOLE_NEGATIVE configuration option\n" "to YES, so that the 'old' interpretation algorithm is used. But be warned that\n" "the result might be incorrect.\n"); bWarningAlreadyEmitted = TRUE; } return NULL; #else const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild; OGRMultiPolygon *poTS = new OGRMultiPolygon(); // collect directed faces for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") ) { // collect next face (psChild->psChild) psFaceChild = GetChildElement(psChild); while( psFaceChild != NULL && !(psFaceChild->eType == CXT_Element && EQUAL(BareGMLElement(psFaceChild->pszValue),"Face")) ) psFaceChild = psFaceChild->psNext; if( psFaceChild == NULL ) continue; OGRMultiLineString *poCollectedGeom = new OGRMultiLineString(); // collect directed edges of the face for( psDirectedEdgeChild = psFaceChild->psChild; psDirectedEdgeChild != NULL; psDirectedEdgeChild = psDirectedEdgeChild->psNext ) { if( psDirectedEdgeChild->eType == CXT_Element && EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") ) { OGRGeometry *poEdgeGeom; poEdgeGeom = GML2OGRGeometry_XMLNode( psDirectedEdgeChild, bGetSecondaryGeometryOption, TRUE ); if( poEdgeGeom == NULL || wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get geometry in directedEdge" ); delete poEdgeGeom; delete poCollectedGeom; delete poTS; return NULL; } poCollectedGeom->addGeometryDirectly( poEdgeGeom ); } } OGRGeometry *poFaceCollectionGeom = NULL; OGRPolygon *poFaceGeom = NULL; //#ifdef HAVE_GEOS poFaceCollectionGeom = poCollectedGeom->Polygonize(); if( poFaceCollectionGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to assemble Edges in Face" ); delete poCollectedGeom; delete poTS; return NULL; } poFaceGeom = GML2FaceExtRing( poFaceCollectionGeom ); //#else // poFaceGeom = (OGRPolygon*) OGRBuildPolygonFromEdges( // (OGRGeometryH) poCollectedGeom, // FALSE, TRUE, 0, NULL); //#endif if( poFaceGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to build Polygon for Face" ); delete poCollectedGeom; delete poTS; return NULL; } else { int iCount = poTS->getNumGeometries(); if( iCount == 0) { /* inserting the first Polygon */ poTS->addGeometryDirectly( poFaceGeom ); } else { /* using Union to add the current Polygon */ OGRGeometry *poUnion = poTS->Union( poFaceGeom ); delete poFaceGeom; delete poTS; if( poUnion == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed Union for TopoSurface" ); return NULL; } if( wkbFlatten( poUnion->getGeometryType()) == wkbPolygon ) { /* forcing to be a MultiPolygon */ poTS = new OGRMultiPolygon(); poTS->addGeometryDirectly(poUnion); } else if( wkbFlatten( poUnion->getGeometryType()) == wkbMultiPolygon ) poTS = (OGRMultiPolygon *)poUnion; else { CPLError( CE_Failure, CPLE_AppDefined, "Unexpected geometry type resulting from Union for TopoSurface" ); delete poUnion; return NULL; } } } delete poFaceCollectionGeom; delete poCollectedGeom; } } return poTS; #endif // HAVE_GEOS } /****************************************************************/ /* applying the FaceHoleNegative = TRUE rules */ /* */ /* - each <TopoSurface> is expected to represent a MultiPolygon */ /* - any <Face> declaring orientation="+" is expected to */ /* represent an Exterior Ring (no holes are allowed) */ /* - any <Face> declaring orientation="-" is expected to */ /* represent an Interior Ring (hole) belonging to the latest */ /* Exterior Ring. */ /* - <Egdes> within the same <Face> are expected to be */ /* arranged in geometrically adjacent and consecutive */ /* sequence. */ /****************************************************************/ if( bGetSecondaryGeometry ) return NULL; const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild; int bFaceOrientation = TRUE; OGRPolygon *poTS = new OGRPolygon(); // collect directed faces for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") ) { bFaceOrientation = GetElementOrientation(psChild); // collect next face (psChild->psChild) psFaceChild = GetChildElement(psChild); while( psFaceChild != NULL && !EQUAL(BareGMLElement(psFaceChild->pszValue),"Face") ) psFaceChild = psFaceChild->psNext; if( psFaceChild == NULL ) continue; OGRLinearRing *poFaceGeom = new OGRLinearRing(); // collect directed edges of the face for( psDirectedEdgeChild = psFaceChild->psChild; psDirectedEdgeChild != NULL; psDirectedEdgeChild = psDirectedEdgeChild->psNext ) { if( psDirectedEdgeChild->eType == CXT_Element && EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") ) { OGRGeometry *poEdgeGeom; poEdgeGeom = GML2OGRGeometry_XMLNode( psDirectedEdgeChild, bGetSecondaryGeometryOption, nRecLevel + 1, TRUE, bFaceOrientation ); if( poEdgeGeom == NULL || wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get geometry in directedEdge" ); delete poEdgeGeom; delete poFaceGeom; delete poTS; return NULL; } OGRLineString *poLS; OGRLineString *poAddLS; if( !bFaceOrientation ) { poLS = (OGRLineString *)poEdgeGeom; poAddLS = (OGRLineString *)poFaceGeom; if( poAddLS->getNumPoints() < 2 ) { /* skip it */ } else if( poLS->getNumPoints() > 0 && fabs(poLS->getX(poLS->getNumPoints()-1) - poAddLS->getX(0)) < 1e-14 && fabs(poLS->getY(poLS->getNumPoints()-1) - poAddLS->getY(0)) < 1e-14 && fabs(poLS->getZ(poLS->getNumPoints()-1) - poAddLS->getZ(0)) < 1e-14) { // Skip the first point of the new linestring to avoid // invalidate duplicate points poLS->addSubLineString( poAddLS, 1 ); } else { // Add the whole new line string poLS->addSubLineString( poAddLS ); } poFaceGeom->empty(); } poLS = (OGRLineString *)poFaceGeom; poAddLS = (OGRLineString *)poEdgeGeom; if( poAddLS->getNumPoints() < 2 ) { /* skip it */ } else if( poLS->getNumPoints() > 0 && fabs(poLS->getX(poLS->getNumPoints()-1) - poAddLS->getX(0)) < 1e-14 && fabs(poLS->getY(poLS->getNumPoints()-1) - poAddLS->getY(0)) < 1e-14 && fabs(poLS->getZ(poLS->getNumPoints()-1) - poAddLS->getZ(0)) < 1e-14) { // Skip the first point of the new linestring to avoid // invalidate duplicate points poLS->addSubLineString( poAddLS, 1 ); } else { // Add the whole new line string poLS->addSubLineString( poAddLS ); } delete poEdgeGeom; } } /* if( poFaceGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get Face geometry in directedFace" ); delete poFaceGeom; return NULL; }*/ poTS->addRingDirectly( poFaceGeom ); } } /* if( poTS == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to get TopoSurface geometry" ); delete poTS; return NULL; }*/ return poTS; } /* -------------------------------------------------------------------- */ /* Surface */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Surface") ) { const CPLXMLNode *psChild; OGRGeometry *poResult = NULL; // Find outer ring. psChild = FindBareXMLChild( psNode, "patches" ); if( psChild == NULL ) psChild = FindBareXMLChild( psNode, "polygonPatches" ); if( psChild == NULL ) psChild = FindBareXMLChild( psNode, "trianglePatches" ); psChild = GetChildElement(psChild); if( psChild == NULL ) { /* <gml:Surface/> and <gml:Surface><gml:patches/></gml:Surface> are valid GML */ return new OGRPolygon(); } for( ; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && (EQUAL(BareGMLElement(psChild->pszValue),"PolygonPatch") || EQUAL(BareGMLElement(psChild->pszValue),"Triangle") || EQUAL(BareGMLElement(psChild->pszValue),"Rectangle"))) { OGRPolygon *poPolygon = (OGRPolygon *) GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); if( poPolygon == NULL ) return NULL; if( poResult == NULL ) poResult = poPolygon; else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon ) { OGRMultiPolygon *poMP = new OGRMultiPolygon(); poMP->addGeometryDirectly( poResult ); poMP->addGeometryDirectly( poPolygon ); poResult = poMP; } else { ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon ); } } } return poResult; } /* -------------------------------------------------------------------- */ /* TriangulatedSurface */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"TriangulatedSurface") || EQUAL(pszBaseGeometry,"Tin") ) { const CPLXMLNode *psChild; OGRGeometry *poResult = NULL; // Find trianglePatches psChild = FindBareXMLChild( psNode, "trianglePatches" ); if (psChild == NULL) psChild = FindBareXMLChild( psNode, "patches" ); psChild = GetChildElement(psChild); if( psChild == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Missing <trianglePatches> for %s.", pszBaseGeometry ); return NULL; } for( ; psChild != NULL; psChild = psChild->psNext ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"Triangle") ) { OGRPolygon *poPolygon = (OGRPolygon *) GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); if( poPolygon == NULL ) return NULL; if( poResult == NULL ) poResult = poPolygon; else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon ) { OGRMultiPolygon *poMP = new OGRMultiPolygon(); poMP->addGeometryDirectly( poResult ); poMP->addGeometryDirectly( poPolygon ); poResult = poMP; } else { ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon ); } } } return poResult; } /* -------------------------------------------------------------------- */ /* Solid */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"Solid") ) { const CPLXMLNode *psChild; OGRGeometry* poGeom; // Find exterior element psChild = FindBareXMLChild( psNode, "exterior"); psChild = GetChildElement(psChild); if( psChild == NULL ) { /* <gml:Solid/> and <gml:Solid><gml:exterior/></gml:Solid> are valid GML */ return new OGRPolygon(); } // Get the geometry inside <exterior> poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); if( poGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior element"); delete poGeom; return NULL; } psChild = FindBareXMLChild( psNode, "interior"); if( psChild != NULL ) { static int bWarnedOnce = FALSE; if (!bWarnedOnce) { CPLError( CE_Warning, CPLE_AppDefined, "<interior> elements of <Solid> are ignored"); bWarnedOnce = TRUE; } } return poGeom; } /* -------------------------------------------------------------------- */ /* OrientableSurface */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"OrientableSurface") ) { const CPLXMLNode *psChild; // Find baseSurface. psChild = FindBareXMLChild( psNode, "baseSurface" ); psChild = GetChildElement(psChild); if( psChild == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Missing <baseSurface> for OrientableSurface." ); return NULL; } return GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, nRecLevel + 1 ); } /* -------------------------------------------------------------------- */ /* SimplePolygon, SimpleRectangle, SimpleTriangle */ /* (GML 3.3 compact encoding) */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"SimplePolygon") || EQUAL(pszBaseGeometry,"SimpleRectangle") || EQUAL(pszBaseGeometry,"SimpleTriangle") ) { OGRLinearRing *poRing = new OGRLinearRing(); if( !ParseGMLCoordinates( psNode, poRing ) ) { delete poRing; return NULL; } poRing->closeRings(); OGRPolygon* poPolygon = new OGRPolygon(); poPolygon->addRingDirectly(poRing); return poPolygon; } /* -------------------------------------------------------------------- */ /* SimpleMultiPoint (GML 3.3 compact encoding) */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"SimpleMultiPoint") ) { OGRLineString *poLS = new OGRLineString(); if( !ParseGMLCoordinates( psNode, poLS ) ) { delete poLS; return NULL; } OGRMultiPoint* poMP = new OGRMultiPoint(); int nPoints = poLS->getNumPoints(); for(int i = 0; i < nPoints; i++) { OGRPoint* poPoint = new OGRPoint(); poLS->getPoint(i, poPoint); poMP->addGeometryDirectly(poPoint); } delete poLS; return poMP; } CPLError( CE_Failure, CPLE_AppDefined, "Unrecognised geometry type <%.500s>.", pszBaseGeometry ); return NULL; } /************************************************************************/ /* OGR_G_CreateFromGMLTree() */ /************************************************************************/ OGRGeometryH OGR_G_CreateFromGMLTree( const CPLXMLNode *psTree ) { return (OGRGeometryH) GML2OGRGeometry_XMLNode( psTree, -1 ); } /************************************************************************/ /* OGR_G_CreateFromGML() */ /************************************************************************/ /** * \brief Create geometry from GML. * * This method translates a fragment of GML containing only the geometry * portion into a corresponding OGRGeometry. There are many limitations * on the forms of GML geometries supported by this parser, but they are * too numerous to list here. * * The following GML2 elements are parsed : Point, LineString, Polygon, * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. * * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface, * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, CompositeCurve, * LineStringSegment, Arc, Circle, CompositeSurface, OrientableSurface, Solid, * Tin, TriangulatedSurface. * * Arc and Circle elements are stroked to linestring, by using a * 4 degrees step, unless the user has overridden the value with the * OGR_ARC_STEPSIZE configuration variable. * * The C++ method OGRGeometryFactory::createFromGML() is the same as this function. * * @param pszGML The GML fragment for the geometry. * * @return a geometry on succes, or NULL on error. */ OGRGeometryH OGR_G_CreateFromGML( const char *pszGML ) { if( pszGML == NULL || strlen(pszGML) == 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "GML Geometry is empty in OGR_G_CreateFromGML()." ); return NULL; } /* ------------------------------------------------------------ -------- */ /* Try to parse the XML snippet using the MiniXML API. If this */ /* fails, we assume the minixml api has already posted a CPL */ /* error, and just return NULL. */ /* -------------------------------------------------------------------- */ CPLXMLNode *psGML = CPLParseXMLString( pszGML ); if( psGML == NULL ) return NULL; /* -------------------------------------------------------------------- */ /* Convert geometry recursively. */ /* -------------------------------------------------------------------- */ OGRGeometry *poGeometry; /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */ int bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); poGeometry = GML2OGRGeometry_XMLNode( psGML, -1, 0, FALSE, TRUE, bFaceHoleNegative ); CPLDestroyXMLNode( psGML ); return (OGRGeometryH) poGeometry; }