mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-19 06:05:31 -06:00
3404 lines
131 KiB
C++
3404 lines
131 KiB
C++
/******************************************************************************
|
|
* $Id: gml2ogrgeometry.cpp 28751 2015-03-20 15:21:49Z 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"
|
|
#include "ogrsf_frmts/xplane/ogr_xplane_geo_utils.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 ||
|
|
eType == wkbCircularString )
|
|
{
|
|
if( nDimension == 3 )
|
|
((OGRSimpleCurve *) poGeometry)->addPoint( dfX, dfY, dfZ );
|
|
else
|
|
((OGRSimpleCurve *) poGeometry)->addPoint( dfX, dfY );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
CPLAssert( FALSE );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ParseGMLCoordinates() */
|
|
/************************************************************************/
|
|
|
|
static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeometry,
|
|
int nSRSDimension )
|
|
|
|
{
|
|
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;
|
|
|
|
const char* pszBareElement = BareGMLElement(psPointPropertyIter->pszValue);
|
|
if (EQUAL(pszBareElement,"Point") || EQUAL(pszBareElement,"ElevatedPoint") )
|
|
{
|
|
OGRPoint oPoint;
|
|
if( ParseGMLCoordinates( psPointPropertyIter, &oPoint, nSRSDimension ) )
|
|
{
|
|
int bSuccess = AddPoint( poGeometry, oPoint.getX(),
|
|
oPoint.getY(), oPoint.getZ(),
|
|
oPoint.getCoordinateDimension() );
|
|
if (bSuccess)
|
|
bHasFoundPosElement = TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( psPos->psChild && psPos->psChild->eType == CXT_Attribute &&
|
|
psPos->psChild->psNext == NULL &&
|
|
strcmp(psPos->psChild->pszValue, "xlink:href") == 0 )
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Cannot resolve xlink:href='%s'. Try setting GML_SKIP_RESOLVE_ELEMS=NONE",
|
|
psPos->psChild->psChild->pszValue);
|
|
}
|
|
|
|
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);
|
|
else if( nSRSDimension != 0 ) /* or use one coming from a still higher level element (#5606) */
|
|
nDimension = nSRSDimension;
|
|
|
|
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_AddToCompositeCurve() */
|
|
/************************************************************************/
|
|
|
|
static
|
|
int GML2OGRGeometry_AddToCompositeCurve(OGRCompoundCurve* poCC,
|
|
OGRGeometry* poGeom,
|
|
int& bChildrenAreAllLineString)
|
|
{
|
|
if( poGeom == NULL ||
|
|
!OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"CompositeCurve: Got %.500s geometry as Member instead of a curve.",
|
|
poGeom ? poGeom->getGeometryName() : "NULL" );
|
|
return FALSE;
|
|
}
|
|
|
|
/* Crazy but allowed by GML: composite in composite */
|
|
if( wkbFlatten(poGeom->getGeometryType()) == wkbCompoundCurve )
|
|
{
|
|
OGRCompoundCurve* poCCChild = (OGRCompoundCurve* ) poGeom;
|
|
while( poCCChild->getNumCurves() != 0 )
|
|
{
|
|
OGRCurve* poCurve = poCCChild->stealCurve(0);
|
|
if( wkbFlatten(poCurve->getGeometryType()) != wkbLineString )
|
|
bChildrenAreAllLineString = FALSE;
|
|
if( poCC->addCurveDirectly( poCurve ) != OGRERR_NONE )
|
|
{
|
|
delete poCurve;
|
|
return FALSE;
|
|
}
|
|
}
|
|
delete poCCChild;
|
|
}
|
|
else
|
|
{
|
|
if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
|
|
bChildrenAreAllLineString = FALSE;
|
|
|
|
if( poCC->addCurveDirectly( (OGRCurve*)poGeom ) != OGRERR_NONE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GML2OGRGeometry_AddToCompositeCurve() */
|
|
/************************************************************************/
|
|
|
|
static
|
|
int GML2OGRGeometry_AddToMultiSurface(OGRMultiSurface* poMS,
|
|
OGRGeometry*& poGeom,
|
|
const char* pszMemberElement,
|
|
int& bChildrenAreAllPolygons)
|
|
{
|
|
if (poGeom == NULL)
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s",
|
|
pszMemberElement );
|
|
return FALSE;
|
|
}
|
|
|
|
OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
|
|
if( eType == wkbPolygon || eType == wkbCurvePolygon )
|
|
{
|
|
if( eType != wkbPolygon )
|
|
bChildrenAreAllPolygons = FALSE;
|
|
|
|
if( poMS->addGeometryDirectly( poGeom ) != OGRERR_NONE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (eType == wkbMultiPolygon || eType == wkbMultiSurface)
|
|
{
|
|
OGRMultiSurface* poMS2 = (OGRMultiSurface*) poGeom;
|
|
int i;
|
|
for(i=0;i<poMS2->getNumGeometries();i++)
|
|
{
|
|
if( wkbFlatten(poMS2->getGeometryRef(i)->getGeometryType()) != wkbPolygon )
|
|
bChildrenAreAllPolygons = FALSE;
|
|
|
|
if( poMS->addGeometry(poMS2->getGeometryRef(i)) != OGRERR_NONE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
delete poGeom;
|
|
poGeom = NULL;
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Got %.500s geometry as %s.",
|
|
poGeom->getGeometryName(), pszMemberElement );
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GML2OGRGeometry_XMLNode() */
|
|
/* */
|
|
/* Translates the passed XMLnode and it's children into an */
|
|
/* OGRGeometry. This is used recursively for geometry */
|
|
/* collections. */
|
|
/************************************************************************/
|
|
|
|
static
|
|
OGRGeometry *GML2OGRGeometry_XMLNode_Internal( const CPLXMLNode *psNode,
|
|
int bGetSecondaryGeometryOption,
|
|
int nRecLevel,
|
|
int nSRSDimension,
|
|
const char* pszSRSName,
|
|
int bIgnoreGSG = FALSE,
|
|
int bOrientation = TRUE,
|
|
int bFaceHoleNegative = FALSE );
|
|
|
|
OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode,
|
|
int bGetSecondaryGeometryOption,
|
|
int nRecLevel,
|
|
int nSRSDimension,
|
|
int bIgnoreGSG,
|
|
int bOrientation,
|
|
int bFaceHoleNegative )
|
|
|
|
{
|
|
return GML2OGRGeometry_XMLNode_Internal(psNode,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel, nSRSDimension,
|
|
NULL,
|
|
bIgnoreGSG, bOrientation,
|
|
bFaceHoleNegative);
|
|
}
|
|
|
|
static
|
|
OGRGeometry *GML2OGRGeometry_XMLNode_Internal( const CPLXMLNode *psNode,
|
|
int bGetSecondaryGeometryOption,
|
|
int nRecLevel,
|
|
int nSRSDimension,
|
|
const char* pszSRSName,
|
|
int bIgnoreGSG,
|
|
int bOrientation,
|
|
int bFaceHoleNegative )
|
|
{
|
|
const int bCastToLinearTypeIfPossible = TRUE; /* hard-coded for now */
|
|
|
|
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* pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psNode, "srsDimension", NULL);
|
|
if( pszSRSDimension != NULL )
|
|
nSRSDimension = atoi(pszSRSDimension);
|
|
|
|
if( pszSRSName == NULL )
|
|
pszSRSName = CPLGetXMLValue( (CPLXMLNode*) psNode, "srsName", 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;
|
|
|
|
// 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 new OGRPolygon();
|
|
}
|
|
|
|
// Translate outer ring and add to polygon.
|
|
OGRGeometry* poGeom =
|
|
GML2OGRGeometry_XMLNode_Internal( psChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension,
|
|
pszSRSName );
|
|
if( poGeom == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior ring");
|
|
return NULL;
|
|
}
|
|
|
|
if( !OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"%s: Got %.500s geometry as outerBoundaryIs.",
|
|
pszBaseGeometry, poGeom->getGeometryName() );
|
|
delete poGeom;
|
|
return NULL;
|
|
}
|
|
|
|
if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
|
|
!EQUAL(poGeom->getGeometryName(), "LINEARRING") )
|
|
{
|
|
poGeom = OGRCurve::CastToLinearRing((OGRCurve*)poGeom);
|
|
}
|
|
|
|
OGRCurvePolygon *poCP;
|
|
int bIsPolygon;
|
|
if( EQUAL(poGeom->getGeometryName(), "LINEARRING") )
|
|
{
|
|
poCP = new OGRPolygon();
|
|
bIsPolygon = TRUE;
|
|
}
|
|
else
|
|
{
|
|
poCP = new OGRCurvePolygon();
|
|
bIsPolygon = FALSE;
|
|
}
|
|
|
|
if( poCP->addRingDirectly( (OGRCurve*)poGeom ) != OGRERR_NONE )
|
|
{
|
|
delete poCP;
|
|
delete poGeom;
|
|
return NULL;
|
|
}
|
|
|
|
// 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)
|
|
poGeom =
|
|
GML2OGRGeometry_XMLNode_Internal( psInteriorChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1,
|
|
nSRSDimension,
|
|
pszSRSName );
|
|
else
|
|
poGeom = NULL;
|
|
if (poGeom == NULL)
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring");
|
|
delete poCP;
|
|
return NULL;
|
|
}
|
|
|
|
if( !OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"%s: Got %.500s geometry as innerBoundaryIs.",
|
|
pszBaseGeometry, poGeom->getGeometryName() );
|
|
delete poCP;
|
|
delete poGeom;
|
|
return NULL;
|
|
}
|
|
|
|
if( bIsPolygon )
|
|
{
|
|
if( !EQUAL(poGeom->getGeometryName(), "LINEARRING") )
|
|
{
|
|
if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
|
|
{
|
|
OGRLineString* poLS = (OGRLineString*)poGeom;
|
|
poGeom = OGRCurve::CastToLinearRing(poLS);
|
|
}
|
|
else
|
|
{
|
|
/* Might fail if some rings are not closed */
|
|
/* We used to be tolerant about that with Polygon */
|
|
/* but we have become stricter with CurvePolygon */
|
|
poCP = OGRSurface::CastToCurvePolygon( (OGRPolygon*)poCP );
|
|
if( poCP == NULL )
|
|
{
|
|
delete poGeom;
|
|
return NULL;
|
|
}
|
|
bIsPolygon = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( EQUAL(poGeom->getGeometryName(), "LINEARRING") )
|
|
poGeom = OGRCurve::CastToLineString( (OGRCurve*)poGeom );
|
|
}
|
|
if( poCP->addRingDirectly( (OGRCurve*)poGeom ) != OGRERR_NONE )
|
|
{
|
|
delete poCP;
|
|
delete poGeom;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return poCP;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* LinearRing */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"LinearRing") )
|
|
{
|
|
OGRLinearRing *poLinearRing = new OGRLinearRing();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poLinearRing, nSRSDimension ) )
|
|
{
|
|
delete poLinearRing;
|
|
return NULL;
|
|
}
|
|
|
|
return poLinearRing;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Ring GML3 */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"Ring") )
|
|
{
|
|
OGRCurve* poRing = NULL;
|
|
OGRCompoundCurve *poCC = NULL;
|
|
int bChildrenAreAllLineString = TRUE;
|
|
const CPLXMLNode *psChild;
|
|
|
|
int bLastCurveWasApproximateArc = FALSE;
|
|
int bLastCurveWasApproximateArcInvertedAxisOrder = FALSE;
|
|
double dfLastCurveApproximateArcRadius = 0;
|
|
|
|
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_Internal( psCurveChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1,
|
|
nSRSDimension,
|
|
pszSRSName );
|
|
else
|
|
{
|
|
if( psChild->psChild && psChild->psChild->eType == CXT_Attribute &&
|
|
psChild->psChild->psNext == NULL &&
|
|
strcmp(psChild->psChild->pszValue, "xlink:href") == 0 )
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Cannot resolve xlink:href='%s'. Try setting GML_SKIP_RESOLVE_ELEMS=NONE",
|
|
psChild->psChild->psChild->pszValue);
|
|
}
|
|
|
|
poGeom = NULL;
|
|
}
|
|
|
|
// try to join multiline string to one linestring
|
|
if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString )
|
|
{
|
|
poGeom = OGRGeometryFactory::forceToLineString( poGeom, false );
|
|
}
|
|
|
|
if( poGeom == NULL
|
|
|| !OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
delete poGeom;
|
|
delete poRing;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
|
|
if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
|
|
bChildrenAreAllLineString = FALSE;
|
|
|
|
/* Ad-hoc logic to handle nicely connecting ArcByCenterPoint */
|
|
/* with consecutive curves, as found in some AIXM files */
|
|
int bIsApproximateArc = FALSE;
|
|
const CPLXMLNode* psChild2, *psChild3;
|
|
if( strcmp(psCurveChild->pszValue, "Curve") == 0 &&
|
|
(psChild2 = GetChildElement(psCurveChild)) != NULL &&
|
|
strcmp(psChild2->pszValue, "segments") == 0 &&
|
|
(psChild3 = GetChildElement(psChild2)) != NULL &&
|
|
strcmp(psChild3->pszValue, "ArcByCenterPoint") == 0 )
|
|
{
|
|
const CPLXMLNode* psRadius = FindBareXMLChild( psChild3, "radius");
|
|
if( psRadius && psRadius->eType == CXT_Element )
|
|
{
|
|
double dfRadius = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psRadius, NULL, "0"));
|
|
const char* pszUnits = CPLGetXMLValue((CPLXMLNode*)psRadius, "uom", NULL);
|
|
int bSRSUnitIsDeegree = FALSE;
|
|
int bInvertedAxisOrder = FALSE;
|
|
if( pszSRSName != NULL )
|
|
{
|
|
OGRSpatialReference oSRS;
|
|
if( oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE )
|
|
{
|
|
if( oSRS.IsGeographic() )
|
|
{
|
|
bInvertedAxisOrder = oSRS.EPSGTreatsAsLatLong();
|
|
bSRSUnitIsDeegree = fabs(oSRS.GetAngularUnits(NULL) - CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
|
|
}
|
|
}
|
|
}
|
|
if( bSRSUnitIsDeegree && pszUnits != NULL &&
|
|
(EQUAL(pszUnits, "m") || EQUAL(pszUnits, "nm") ||
|
|
EQUAL(pszUnits, "mi") || EQUAL(pszUnits, "ft")) )
|
|
{
|
|
bIsApproximateArc = TRUE;
|
|
if( EQUAL(pszUnits, "nm") )
|
|
dfRadius *= CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV);
|
|
else if( EQUAL(pszUnits, "mi") )
|
|
dfRadius *= CPLAtof(SRS_UL_INTL_STAT_MILE_CONV);
|
|
else if( EQUAL(pszUnits, "ft") )
|
|
dfRadius *= CPLAtof(SRS_UL_INTL_FOOT_CONV);
|
|
dfLastCurveApproximateArcRadius = dfRadius;
|
|
bLastCurveWasApproximateArcInvertedAxisOrder = bInvertedAxisOrder;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( poCC == NULL && poRing == NULL )
|
|
poRing = (OGRCurve*)poGeom;
|
|
else
|
|
{
|
|
if( poCC == NULL )
|
|
{
|
|
poCC = new OGRCompoundCurve();
|
|
if( poCC->addCurveDirectly(poRing) != OGRERR_NONE )
|
|
{
|
|
delete poGeom;
|
|
delete poRing;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
poRing = NULL;
|
|
}
|
|
|
|
if( bIsApproximateArc )
|
|
{
|
|
if( poGeom->getGeometryType() == wkbLineString )
|
|
{
|
|
OGRCurve* poPreviousCurve = poCC->getCurve(poCC->getNumCurves()-1);
|
|
OGRLineString* poLS = (OGRLineString*)poGeom;
|
|
if( poPreviousCurve->getNumPoints() >= 2 && poLS->getNumPoints() >= 2 )
|
|
{
|
|
OGRPoint p, p2;
|
|
poPreviousCurve->EndPoint(&p);
|
|
poLS->StartPoint(&p2);
|
|
double dfDistance;
|
|
if( bLastCurveWasApproximateArcInvertedAxisOrder )
|
|
dfDistance = OGRXPlane_Distance(p.getX(), p.getY(), p2.getX(), p2.getY());
|
|
else
|
|
dfDistance = OGRXPlane_Distance(p.getY(), p.getX(), p2.getY(), p2.getX());
|
|
//CPLDebug("OGR", "%f %f\n", dfDistance, dfLastCurveApproximateArcRadius / 10 );
|
|
if( dfDistance < dfLastCurveApproximateArcRadius / 5 )
|
|
{
|
|
CPLDebug("OGR", "Moving approximate start of ArcByCenterPoint to end of previous curve");
|
|
poLS->setPoint(0, &p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( bLastCurveWasApproximateArc )
|
|
{
|
|
OGRCurve* poPreviousCurve = poCC->getCurve(poCC->getNumCurves()-1);
|
|
if( poPreviousCurve->getGeometryType() == wkbLineString )
|
|
{
|
|
OGRLineString* poLS = (OGRLineString*)poPreviousCurve;
|
|
if( poLS->getNumPoints() >= 2 && ((OGRCurve*)poGeom)->getNumPoints() >= 2 )
|
|
{
|
|
OGRPoint p, p2;
|
|
((OGRCurve*)poGeom)->StartPoint(&p);
|
|
poLS->EndPoint(&p2);
|
|
double dfDistance;
|
|
if( bLastCurveWasApproximateArcInvertedAxisOrder )
|
|
dfDistance = OGRXPlane_Distance(p.getX(), p.getY(), p2.getX(), p2.getY());
|
|
else
|
|
dfDistance = OGRXPlane_Distance(p.getY(), p.getX(), p2.getY(), p2.getX());
|
|
//CPLDebug("OGR", "%f %f\n", dfDistance, dfLastCurveApproximateArcRadius / 10 );
|
|
// "A-311 WHEELER AFB OAHU, HI.xml" needs more than 10%
|
|
if( dfDistance < dfLastCurveApproximateArcRadius / 5 )
|
|
{
|
|
CPLDebug("OGR", "Moving approximate end of last ArcByCenterPoint to start of current curve");
|
|
poLS->setPoint(poLS->getNumPoints()-1, &p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( poCC->addCurveDirectly((OGRCurve*)poGeom) != OGRERR_NONE )
|
|
{
|
|
delete poGeom;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bLastCurveWasApproximateArc = bIsApproximateArc;
|
|
}
|
|
}
|
|
|
|
if( poRing )
|
|
{
|
|
if( poRing->getNumPoints() < 2 || !poRing->get_IsClosed() )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Non-closed ring");
|
|
delete poRing;
|
|
return NULL;
|
|
}
|
|
return poRing;
|
|
}
|
|
|
|
if( poCC == NULL )
|
|
return NULL;
|
|
|
|
else if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString )
|
|
{
|
|
return OGRCurve::CastToLinearRing(poCC);
|
|
}
|
|
else
|
|
{
|
|
if( poCC->getNumPoints() < 2 || !poCC->get_IsClosed() )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Non-closed ring");
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
return poCC;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* LineString */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"LineString")
|
|
|| EQUAL(pszBaseGeometry,"LineStringSegment")
|
|
|| EQUAL(pszBaseGeometry,"GeodesicString") )
|
|
{
|
|
OGRLineString *poLine = new OGRLineString();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poLine, nSRSDimension ) )
|
|
{
|
|
delete poLine;
|
|
return NULL;
|
|
}
|
|
|
|
return poLine;
|
|
}
|
|
|
|
#if 0
|
|
/* -------------------------------------------------------------------- */
|
|
/* Arc/Circle : we approximate them by linear segments */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"Arc") ||
|
|
EQUAL(pszBaseGeometry,"Circle") )
|
|
{
|
|
OGRLineString *poLine = new OGRLineString();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poLine, nSRSDimension ) ||
|
|
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 = CPLAtof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")) / 180 * PI;
|
|
|
|
// make sure the segments are not too short
|
|
double dfMinStepLength = CPLAtof( 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;
|
|
}
|
|
#endif
|
|
/* -------------------------------------------------------------------- */
|
|
/* Arc */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"Arc") )
|
|
{
|
|
OGRCircularString *poCC = new OGRCircularString();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poCC, nSRSDimension ) )
|
|
{
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
|
|
if( poCC->getNumPoints() != 3 )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in Arc");
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* ArcString */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"ArcString") )
|
|
{
|
|
OGRCircularString *poCC = new OGRCircularString();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poCC, nSRSDimension ) )
|
|
{
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
|
|
if( poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1 )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in ArcString");
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Circle */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"Circle") )
|
|
{
|
|
OGRLineString *poLine = new OGRLineString();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poLine, nSRSDimension ) )
|
|
{
|
|
delete poLine;
|
|
return NULL;
|
|
}
|
|
|
|
if( poLine->getNumPoints() != 3 )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in Circle");
|
|
delete poLine;
|
|
return NULL;
|
|
}
|
|
|
|
double R, cx, cy, alpha0, alpha1, alpha2;
|
|
if( !OGRGeometryFactory::GetCurveParmeters(
|
|
poLine->getX(0), poLine->getY(0),
|
|
poLine->getX(1), poLine->getY(1),
|
|
poLine->getX(2), poLine->getY(2),
|
|
R, cx, cy, alpha0, alpha1, alpha2 ) )
|
|
{
|
|
delete poLine;
|
|
return NULL;
|
|
}
|
|
|
|
OGRCircularString *poCC = new OGRCircularString();
|
|
OGRPoint p;
|
|
poLine->getPoint(0, &p);
|
|
poCC->addPoint(&p);
|
|
poLine->getPoint(1, &p);
|
|
poCC->addPoint(&p);
|
|
poLine->getPoint(2, &p);
|
|
poCC->addPoint(&p);
|
|
double alpha4 = (alpha2 > alpha0) ? alpha0 + 2 * M_PI : alpha0 - 2 * M_PI;
|
|
double alpha3 = (alpha2 + alpha4) / 2;
|
|
double x = cx + R * cos(alpha3);
|
|
double y = cy + R * sin(alpha3);
|
|
if( poCC->getCoordinateDimension() == 3 )
|
|
poCC->addPoint( x, y, p.getZ() );
|
|
else
|
|
poCC->addPoint( x, y );
|
|
poLine->getPoint(0, &p);
|
|
poCC->addPoint(&p);
|
|
delete poLine;
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* ArcByBulge */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"ArcByBulge") )
|
|
{
|
|
const CPLXMLNode *psChild;
|
|
|
|
psChild = FindBareXMLChild( psNode, "bulge");
|
|
if( psChild == NULL || psChild->eType != CXT_Element )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Missing bulge element." );
|
|
return NULL;
|
|
}
|
|
double dfBulge = CPLAtof(psChild->psChild->pszValue);
|
|
|
|
psChild = FindBareXMLChild( psNode, "normal");
|
|
if( psChild == NULL || psChild->eType != CXT_Element )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Missing normal element." );
|
|
return NULL;
|
|
}
|
|
double dfNormal = CPLAtof(psChild->psChild->pszValue);
|
|
|
|
|
|
OGRLineString* poLS = new OGRLineString();
|
|
if( !ParseGMLCoordinates( psNode, poLS, nSRSDimension ) )
|
|
{
|
|
delete poLS;
|
|
return NULL;
|
|
}
|
|
|
|
if( poLS->getNumPoints() != 2 )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in ArcByBulge");
|
|
delete poLS;
|
|
return NULL;
|
|
}
|
|
|
|
OGRCircularString *poCC = new OGRCircularString();
|
|
OGRPoint p;
|
|
poLS->getPoint(0, &p);
|
|
poCC->addPoint(&p);
|
|
|
|
double dfMidX = (poLS->getX(0) + poLS->getX(1)) / 2;
|
|
double dfMidY = (poLS->getY(0) + poLS->getY(1)) / 2;
|
|
double dfDirX = (poLS->getX(1) - poLS->getX(0)) / 2;
|
|
double dfDirY = (poLS->getY(1) - poLS->getY(0)) / 2;
|
|
double dfNormX = -dfDirY;
|
|
double dfNormY = dfDirX;
|
|
double dfNorm = sqrt(dfNormX * dfNormX + dfNormY * dfNormY);
|
|
if( dfNorm )
|
|
{
|
|
dfNormX /= dfNorm;
|
|
dfNormY /= dfNorm;
|
|
}
|
|
double dfNewX = dfMidX + dfNormX * dfBulge * dfNormal;
|
|
double dfNewY = dfMidY + dfNormY * dfBulge * dfNormal;
|
|
|
|
if( poCC->getCoordinateDimension() == 3 )
|
|
poCC->addPoint( dfNewX, dfNewY, p.getZ() );
|
|
else
|
|
poCC->addPoint( dfNewX, dfNewY );
|
|
|
|
poLS->getPoint(1, &p);
|
|
poCC->addPoint(&p);
|
|
|
|
delete poLS;
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* ArcByCenterPoint */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"ArcByCenterPoint") )
|
|
{
|
|
const CPLXMLNode *psChild;
|
|
|
|
psChild = FindBareXMLChild( psNode, "radius");
|
|
if( psChild == NULL || psChild->eType != CXT_Element )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Missing radius element." );
|
|
return NULL;
|
|
}
|
|
double dfRadius = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0"));
|
|
const char* pszUnits = CPLGetXMLValue((CPLXMLNode*)psChild, "uom", NULL);
|
|
|
|
psChild = FindBareXMLChild( psNode, "startAngle");
|
|
if( psChild == NULL || psChild->eType != CXT_Element )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Missing startAngle element." );
|
|
return NULL;
|
|
}
|
|
double dfStartAngle = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0"));
|
|
|
|
psChild = FindBareXMLChild( psNode, "endAngle");
|
|
if( psChild == NULL || psChild->eType != CXT_Element )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Missing endAngle element." );
|
|
return NULL;
|
|
}
|
|
double dfEndAngle = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0"));
|
|
|
|
OGRPoint p;
|
|
if( !ParseGMLCoordinates( psNode, &p, nSRSDimension ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int bSRSUnitIsDeegree = FALSE;
|
|
int bInvertedAxisOrder = FALSE;
|
|
if( pszSRSName != NULL )
|
|
{
|
|
OGRSpatialReference oSRS;
|
|
if( oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE )
|
|
{
|
|
if( oSRS.IsGeographic() )
|
|
{
|
|
bInvertedAxisOrder = oSRS.EPSGTreatsAsLatLong();
|
|
bSRSUnitIsDeegree = fabs(oSRS.GetAngularUnits(NULL) - CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
|
|
}
|
|
}
|
|
}
|
|
|
|
double dfCenterX = p.getX();
|
|
double dfCenterY = p.getY();
|
|
|
|
if( bSRSUnitIsDeegree && pszUnits != NULL &&
|
|
(EQUAL(pszUnits, "m") || EQUAL(pszUnits, "nm") ||
|
|
EQUAL(pszUnits, "mi") || EQUAL(pszUnits, "ft")) )
|
|
{
|
|
OGRLineString* poLS = new OGRLineString();
|
|
double dfStep = CPLAtof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4"));
|
|
double dfDistance = dfRadius;
|
|
if( EQUAL(pszUnits, "nm") )
|
|
dfDistance *= CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV);
|
|
else if( EQUAL(pszUnits, "mi") )
|
|
dfDistance *= CPLAtof(SRS_UL_INTL_STAT_MILE_CONV);
|
|
else if( EQUAL(pszUnits, "ft") )
|
|
dfDistance *= CPLAtof(SRS_UL_INTL_FOOT_CONV);
|
|
double dfSign = (dfStartAngle < dfEndAngle) ? 1 : -1;
|
|
for(double dfAngle = dfStartAngle; (dfAngle - dfEndAngle) * dfSign < 0; dfAngle += dfSign * dfStep)
|
|
{
|
|
double dfLong, dfLat;
|
|
if( bInvertedAxisOrder )
|
|
{
|
|
OGRXPlane_ExtendPosition(dfCenterX, dfCenterY,
|
|
dfDistance, 90-dfAngle, /* not sure of angle conversion here...*/
|
|
&dfLat, &dfLong);
|
|
p.setY( dfLat );
|
|
p.setX( dfLong );
|
|
}
|
|
else
|
|
{
|
|
OGRXPlane_ExtendPosition(dfCenterY, dfCenterX,
|
|
dfDistance, 90-dfAngle,
|
|
&dfLat, &dfLong);
|
|
p.setX( dfLong );
|
|
p.setY( dfLat );
|
|
}
|
|
poLS->addPoint(&p);
|
|
}
|
|
|
|
double dfLong, dfLat;
|
|
if( bInvertedAxisOrder )
|
|
{
|
|
OGRXPlane_ExtendPosition(dfCenterX, dfCenterY,
|
|
dfDistance, 90-dfEndAngle, /* not sure of angle conversion here...*/
|
|
&dfLat, &dfLong);
|
|
p.setY( dfLat );
|
|
p.setX( dfLong );
|
|
}
|
|
else
|
|
{
|
|
OGRXPlane_ExtendPosition(dfCenterY, dfCenterX,
|
|
dfDistance, 90-dfEndAngle,
|
|
&dfLat, &dfLong);
|
|
p.setX( dfLong );
|
|
p.setY( dfLat );
|
|
}
|
|
poLS->addPoint(&p);
|
|
|
|
return poLS;
|
|
}
|
|
|
|
OGRCircularString *poCC = new OGRCircularString();
|
|
p.setX( dfCenterX + dfRadius * cos(dfStartAngle * M_PI / 180.0) );
|
|
p.setY( dfCenterY + dfRadius * sin(dfStartAngle * M_PI / 180.0) );
|
|
poCC->addPoint(&p);
|
|
p.setX( dfCenterX + dfRadius * cos((dfStartAngle+dfEndAngle)/2 * M_PI / 180.0) );
|
|
p.setY( dfCenterY + dfRadius * sin((dfStartAngle+dfEndAngle)/2 * M_PI / 180.0) );
|
|
poCC->addPoint(&p);
|
|
p.setX( dfCenterX + dfRadius * cos(dfEndAngle * M_PI / 180.0) );
|
|
p.setY( dfCenterY + dfRadius * sin(dfEndAngle * M_PI / 180.0) );
|
|
poCC->addPoint(&p);
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* CircleByCenterPoint */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"CircleByCenterPoint") )
|
|
{
|
|
const CPLXMLNode *psChild;
|
|
|
|
psChild = FindBareXMLChild( psNode, "radius");
|
|
if( psChild == NULL || psChild->eType != CXT_Element )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Missing radius element." );
|
|
return NULL;
|
|
}
|
|
double dfRadius = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0"));
|
|
const char* pszUnits = CPLGetXMLValue((CPLXMLNode*)psChild, "uom", NULL);
|
|
|
|
OGRPoint p;
|
|
if( !ParseGMLCoordinates( psNode, &p, nSRSDimension ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int bSRSUnitIsDeegree = FALSE;
|
|
int bInvertedAxisOrder = FALSE;
|
|
if( pszSRSName != NULL )
|
|
{
|
|
OGRSpatialReference oSRS;
|
|
if( oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE )
|
|
{
|
|
if( oSRS.IsGeographic() )
|
|
{
|
|
bInvertedAxisOrder = oSRS.EPSGTreatsAsLatLong();
|
|
bSRSUnitIsDeegree = fabs(oSRS.GetAngularUnits(NULL) - CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
|
|
}
|
|
}
|
|
}
|
|
|
|
double dfCenterX = p.getX();
|
|
double dfCenterY = p.getY();
|
|
|
|
if( bSRSUnitIsDeegree && pszUnits != NULL &&
|
|
(EQUAL(pszUnits, "m") || EQUAL(pszUnits, "nm") ||
|
|
EQUAL(pszUnits, "mi") || EQUAL(pszUnits, "ft")) )
|
|
{
|
|
OGRLineString* poLS = new OGRLineString();
|
|
double dfStep = CPLAtof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4"));
|
|
double dfDistance = dfRadius;
|
|
if( EQUAL(pszUnits, "nm") )
|
|
dfDistance *= CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV);
|
|
else if( EQUAL(pszUnits, "mi") )
|
|
dfDistance *= CPLAtof(SRS_UL_INTL_STAT_MILE_CONV);
|
|
else if( EQUAL(pszUnits, "ft") )
|
|
dfDistance *= CPLAtof(SRS_UL_INTL_FOOT_CONV);
|
|
for(double dfAngle = 0; dfAngle < 360; dfAngle += dfStep)
|
|
{
|
|
double dfLong, dfLat;
|
|
if( bInvertedAxisOrder )
|
|
{
|
|
OGRXPlane_ExtendPosition(dfCenterX, dfCenterY,
|
|
dfDistance, dfAngle,
|
|
&dfLat, &dfLong);
|
|
p.setY( dfLat );
|
|
p.setX( dfLong );
|
|
}
|
|
else
|
|
{
|
|
OGRXPlane_ExtendPosition(dfCenterY, dfCenterX,
|
|
dfDistance, dfAngle,
|
|
&dfLat, &dfLong);
|
|
p.setX( dfLong );
|
|
p.setY( dfLat );
|
|
}
|
|
poLS->addPoint(&p);
|
|
}
|
|
poLS->getPoint(0, &p);
|
|
poLS->addPoint(&p);
|
|
return poLS;
|
|
}
|
|
|
|
OGRCircularString *poCC = new OGRCircularString();
|
|
p.setX( dfCenterX - dfRadius );
|
|
p.setY( dfCenterY );
|
|
poCC->addPoint(&p);
|
|
p.setX( dfCenterX + dfRadius);
|
|
p.setY( dfCenterY );
|
|
poCC->addPoint(&p);
|
|
p.setX( dfCenterX - dfRadius );
|
|
p.setY( dfCenterY );
|
|
poCC->addPoint(&p);
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* PointType */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"PointType")
|
|
|| EQUAL(pszBaseGeometry,"Point")
|
|
|| EQUAL(pszBaseGeometry,"ConnectionPoint") )
|
|
{
|
|
OGRPoint *poPoint = new OGRPoint();
|
|
|
|
if( !ParseGMLCoordinates( psNode, poPoint, nSRSDimension ) )
|
|
{
|
|
delete poPoint;
|
|
return NULL;
|
|
}
|
|
|
|
return poPoint;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Box */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"BoxType") || EQUAL(pszBaseGeometry,"Box") )
|
|
{
|
|
OGRLineString oPoints;
|
|
|
|
if( !ParseGMLCoordinates( psNode, &oPoints, nSRSDimension ) )
|
|
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;
|
|
OGRMultiSurface* poMS;
|
|
if( EQUAL(pszBaseGeometry,"MultiPolygon") )
|
|
poMS = new OGRMultiPolygon();
|
|
else
|
|
poMS = new OGRMultiSurface();
|
|
int bReconstructTopology = FALSE;
|
|
int bChildrenAreAllPolygons = TRUE;
|
|
|
|
// Iterate over children
|
|
for( psChild = psNode->psChild;
|
|
psChild != NULL;
|
|
psChild = psChild->psNext )
|
|
{
|
|
const char* pszMemberElement = BareGMLElement(psChild->pszValue);
|
|
if( psChild->eType == CXT_Element
|
|
&& (EQUAL(pszMemberElement,"polygonMember") ||
|
|
EQUAL(pszMemberElement,"surfaceMember")) )
|
|
{
|
|
const CPLXMLNode* psSurfaceChild = GetChildElement(psChild);
|
|
|
|
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_Internal(
|
|
psInteriorChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
else
|
|
poRing = NULL;
|
|
if (poRing == NULL)
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring");
|
|
delete poMS;
|
|
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 poMS;
|
|
return NULL;
|
|
}
|
|
|
|
bReconstructTopology = TRUE;
|
|
OGRPolygon *poPolygon = new OGRPolygon();
|
|
poPolygon->addRingDirectly( poRing );
|
|
poMS->addGeometryDirectly( poPolygon );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OGRGeometry* poGeom =
|
|
GML2OGRGeometry_XMLNode_Internal( psSurfaceChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( !GML2OGRGeometry_AddToMultiSurface(poMS, poGeom,
|
|
pszMemberElement,
|
|
bChildrenAreAllPolygons) )
|
|
{
|
|
delete poGeom;
|
|
delete poMS;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (psChild->eType == CXT_Element
|
|
&& EQUAL(pszMemberElement,"surfaceMembers") )
|
|
{
|
|
const CPLXMLNode *psChild2;
|
|
for( psChild2 = psChild->psChild;
|
|
psChild2 != NULL;
|
|
psChild2 = psChild2->psNext )
|
|
{
|
|
pszMemberElement = BareGMLElement(psChild2->pszValue);
|
|
if( psChild2->eType == CXT_Element
|
|
&& (EQUAL(pszMemberElement,"Surface") ||
|
|
EQUAL(pszMemberElement,"Polygon") ||
|
|
EQUAL(pszMemberElement,"PolygonPatch") ||
|
|
EQUAL(pszMemberElement,"CompositeSurface")) )
|
|
{
|
|
OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal(
|
|
psChild2, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( !GML2OGRGeometry_AddToMultiSurface(poMS, poGeom,
|
|
pszMemberElement,
|
|
bChildrenAreAllPolygons) )
|
|
{
|
|
delete poGeom;
|
|
delete poMS;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bReconstructTopology && bChildrenAreAllPolygons )
|
|
{
|
|
OGRMultiPolygon* poMPoly;
|
|
if( wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface )
|
|
poMPoly = OGRMultiSurface::CastToMultiPolygon(poMS);
|
|
else
|
|
poMPoly = (OGRMultiPolygon*)poMS;
|
|
CPLAssert(poMPoly); /* that should not fail really ! */
|
|
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
|
|
{
|
|
if( bCastToLinearTypeIfPossible &&
|
|
wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface &&
|
|
bChildrenAreAllPolygons )
|
|
{
|
|
return OGRMultiSurface::CastToMultiPolygon(poMS);
|
|
}
|
|
else
|
|
return poMS;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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_Internal( psPointChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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_Internal(
|
|
psChild2, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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_Internal( psLineStringChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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 */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"MultiCurve") )
|
|
{
|
|
const CPLXMLNode *psChild;
|
|
OGRMultiCurve *poMC = new OGRMultiCurve();
|
|
int bChildrenAreAllLineString = TRUE;
|
|
|
|
// collect curveMembers
|
|
for( psChild = psNode->psChild;
|
|
psChild != NULL;
|
|
psChild = psChild->psNext )
|
|
{
|
|
if( psChild->eType == CXT_Element
|
|
&& EQUAL(BareGMLElement(psChild->pszValue),"curveMember") )
|
|
{
|
|
const CPLXMLNode *psChild2 = GetChildElement(psChild);
|
|
if( psChild2 != NULL ) /* empty curveMember is valid */
|
|
{
|
|
OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal(
|
|
psChild2, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( poGeom == NULL ||
|
|
!OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"MultiCurve: Got %.500s geometry as Member instead of a curve.",
|
|
poGeom ? poGeom->getGeometryName() : "NULL" );
|
|
if( poGeom != NULL ) delete poGeom;
|
|
delete poMC;
|
|
return NULL;
|
|
}
|
|
|
|
if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
|
|
bChildrenAreAllLineString = FALSE;
|
|
|
|
if( poMC->addGeometryDirectly( poGeom ) != OGRERR_NONE )
|
|
{
|
|
delete 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 )
|
|
{
|
|
OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal(
|
|
psChild2, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if (poGeom == NULL ||
|
|
!OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"MultiCurve: Got %.500s geometry as Member instead of a curve.",
|
|
poGeom ? poGeom->getGeometryName() : "NULL" );
|
|
if( poGeom != NULL ) delete poGeom;
|
|
delete poMC;
|
|
return NULL;
|
|
}
|
|
|
|
if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
|
|
bChildrenAreAllLineString = FALSE;
|
|
|
|
if( poMC->addGeometryDirectly( poGeom ) != OGRERR_NONE )
|
|
{
|
|
delete poGeom;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString )
|
|
{
|
|
return OGRMultiCurve::CastToMultiLineString(poMC);
|
|
}
|
|
else
|
|
return poMC;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* CompositeCurve */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszBaseGeometry,"CompositeCurve") )
|
|
{
|
|
const CPLXMLNode *psChild;
|
|
OGRCompoundCurve *poCC = new OGRCompoundCurve();
|
|
int bChildrenAreAllLineString = TRUE;
|
|
|
|
// collect curveMembers
|
|
for( psChild = psNode->psChild;
|
|
psChild != NULL;
|
|
psChild = psChild->psNext )
|
|
{
|
|
if( psChild->eType == CXT_Element
|
|
&& EQUAL(BareGMLElement(psChild->pszValue),"curveMember") )
|
|
{
|
|
const CPLXMLNode *psChild2 = GetChildElement(psChild);
|
|
if( psChild2 != NULL ) /* empty curveMember is valid */
|
|
{
|
|
OGRGeometry*poGeom = GML2OGRGeometry_XMLNode_Internal(
|
|
psChild2, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( !GML2OGRGeometry_AddToCompositeCurve(poCC, poGeom,
|
|
bChildrenAreAllLineString) )
|
|
{
|
|
delete poGeom;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
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 )
|
|
{
|
|
OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal(
|
|
psChild2, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( !GML2OGRGeometry_AddToCompositeCurve(poCC, poGeom,
|
|
bChildrenAreAllLineString) )
|
|
{
|
|
delete poGeom;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString )
|
|
{
|
|
return OGRCurve::CastToLineString(poCC);
|
|
}
|
|
else
|
|
return poCC;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( poGeom == NULL ||
|
|
!OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
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;
|
|
OGRCurve* poCurve = NULL;
|
|
OGRCompoundCurve *poCC = NULL;
|
|
int bChildrenAreAllLineString = TRUE;
|
|
|
|
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_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( poGeom == NULL ||
|
|
!OGR_GT_IsCurve(poGeom->getGeometryType()) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"segments: Got %.500s geometry as Member instead of curve.",
|
|
poGeom ? poGeom->getGeometryName() : "NULL" );
|
|
delete poGeom;
|
|
delete poCurve;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
|
|
if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
|
|
bChildrenAreAllLineString = FALSE;
|
|
|
|
if( poCC == NULL && poCurve == NULL )
|
|
poCurve = (OGRCurve*)poGeom;
|
|
else
|
|
{
|
|
if( poCC == NULL )
|
|
{
|
|
poCC = new OGRCompoundCurve();
|
|
if( poCC->addCurveDirectly(poCurve) != OGRERR_NONE )
|
|
{
|
|
delete poGeom;
|
|
delete poCurve;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
poCurve = NULL;
|
|
}
|
|
|
|
if( poCC->addCurveDirectly((OGRCurve*)poGeom) != OGRERR_NONE )
|
|
{
|
|
delete poGeom;
|
|
delete poCC;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( poCurve != NULL )
|
|
return poCurve;
|
|
if( poCC == NULL )
|
|
return NULL;
|
|
|
|
if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString )
|
|
{
|
|
return OGRCurve::CastToLineString(poCC);
|
|
}
|
|
else
|
|
{
|
|
return poCC;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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_Internal(
|
|
psGeometryChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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_Internal(
|
|
psPoint, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName, 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_Internal(
|
|
psPoint, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName, 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_Internal(
|
|
psCurve, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName, 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_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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_Internal( psDirectedEdgeChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1,
|
|
nSRSDimension,
|
|
pszSRSName,
|
|
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_Internal( psDirectedEdgeChild,
|
|
bGetSecondaryGeometryOption,
|
|
nRecLevel + 1,
|
|
nSRSDimension,
|
|
pszSRSName,
|
|
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;
|
|
|
|
// 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();
|
|
}
|
|
|
|
OGRMultiSurface* poMS = NULL;
|
|
OGRGeometry* poResult = NULL;
|
|
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")))
|
|
{
|
|
OGRGeometry *poGeom =
|
|
GML2OGRGeometry_XMLNode_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
if( poGeom == NULL )
|
|
{
|
|
delete poResult;
|
|
return NULL;
|
|
}
|
|
|
|
OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
|
|
|
|
if( poResult == NULL )
|
|
poResult = poGeom;
|
|
else
|
|
{
|
|
if( poMS == NULL )
|
|
{
|
|
if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon &&
|
|
eGeomType == wkbPolygon )
|
|
poMS = new OGRMultiPolygon();
|
|
else
|
|
poMS = new OGRMultiSurface();
|
|
#ifdef DEBUG
|
|
OGRErr eErr =
|
|
#endif
|
|
poMS->addGeometryDirectly( poResult );
|
|
CPLAssert(eErr == OGRERR_NONE);
|
|
poResult = poMS;
|
|
}
|
|
else if( eGeomType != wkbPolygon &&
|
|
wkbFlatten(poMS->getGeometryType()) == wkbMultiPolygon )
|
|
{
|
|
poMS = OGRMultiPolygon::CastToMultiSurface((OGRMultiPolygon*)poMS);
|
|
poResult = poMS;
|
|
}
|
|
#ifdef DEBUG
|
|
OGRErr eErr =
|
|
#endif
|
|
poMS->addGeometryDirectly( poGeom );
|
|
CPLAssert(eErr == OGRERR_NONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
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_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
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_Internal(
|
|
psChild, bGetSecondaryGeometryOption,
|
|
nRecLevel + 1, nSRSDimension, pszSRSName );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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, nSRSDimension ) )
|
|
{
|
|
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, nSRSDimension ) )
|
|
{
|
|
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, 0, FALSE, TRUE, bFaceHoleNegative );
|
|
|
|
CPLDestroyXMLNode( psGML );
|
|
|
|
return (OGRGeometryH) poGeometry;
|
|
}
|
|
|
|
|