Skip to content

Commit

Permalink
QgsGML: handle more type of geometries (Arc, CompositeSurface, etc...…
Browse files Browse the repository at this point in the history
…) through OGR
  • Loading branch information
rouault committed Jun 10, 2016
1 parent a4f1511 commit 690e554
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 13 deletions.
106 changes: 93 additions & 13 deletions src/core/qgsgml.cpp
Expand Up @@ -32,6 +32,8 @@
#include <QSettings>
#include <QUrl>

#include "ogr_api.h"

#include <limits>

static const char NS_SEPARATOR = '?';
Expand Down Expand Up @@ -288,6 +290,7 @@ QgsGmlStreamingParser::QgsGmlStreamingParser( const QString& typeName,
, mInvertAxisOrientation( invertAxisOrientation )
, mNumberReturned( -1 )
, mNumberMatched( -1 )
, mFoundUnhandledGeometryElement( false )
{
mThematicAttributes.clear();
for ( int i = 0; i < fields.size(); i++ )
Expand Down Expand Up @@ -348,6 +351,7 @@ QgsGmlStreamingParser::QgsGmlStreamingParser( const QList<LayerProperties>& laye
, mInvertAxisOrientation( invertAxisOrientation )
, mNumberReturned( -1 )
, mNumberMatched( -1 )
, mFoundUnhandledGeometryElement( false )
{
mThematicAttributes.clear();
for ( int i = 0; i < fields.size(); i++ )
Expand Down Expand Up @@ -478,6 +482,24 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a

const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
bool isGeom = false;

if ( theParseMode == geometry || theParseMode == coordinate || theParseMode == posList ||
theParseMode == multiPoint || theParseMode == multiLine || theParseMode == multiPolygon )
{
mGeometryString.append( "<", 1 );
mGeometryString.append( pszLocalName, localNameLen );
mGeometryString.append( " ", 1 );
for ( const XML_Char** attrIter = attr; attrIter && *attrIter; attrIter += 2 )
{
mGeometryString.append( attrIter[0] );
mGeometryString.append( "=\"", 2 );
mGeometryString.append( attrIter[1] );
mGeometryString.append( "\" ", 2 );

}
mGeometryString.append( ">", 1 );
}

if ( isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
{
mParseModeStack.push( coordinate );
Expand Down Expand Up @@ -515,6 +537,8 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
{
mParseModeStack.push( QgsGmlStreamingParser::geometry );
mFoundUnhandledGeometryElement = false;
mGeometryString.clear();
}
//else if ( mParseModeStack.size() == 0 && elementName == mGMLNameSpaceURI + NS_SEPARATOR + "boundedBy" )
else if ( isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
Expand Down Expand Up @@ -739,6 +763,27 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
// e.g: http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=cp:CadastralParcel
mTruncatedResponse = true;
}
else if ( !mGeometryString.empty() &&
!LOCALNAME_EQUALS( "exterior" ) &&
!LOCALNAME_EQUALS( "interior" ) &&
!LOCALNAME_EQUALS( "innerBoundaryIs" ) &&
!LOCALNAME_EQUALS( "outerBoundaryIs" ) &&
!LOCALNAME_EQUALS( "LinearRing" ) &&
!LOCALNAME_EQUALS( "pointMember" ) &&
!LOCALNAME_EQUALS( "curveMember" ) &&
!LOCALNAME_EQUALS( "lineStringMember" ) &&
!LOCALNAME_EQUALS( "polygonMember" ) &&
!LOCALNAME_EQUALS( "surfaceMember" ) &&
!LOCALNAME_EQUALS( "Curve" ) &&
!LOCALNAME_EQUALS( "segments" ) &&
!LOCALNAME_EQUALS( "LineStringSegment" ) )
{
//QgsDebugMsg( "Found unhandled geometry element " + QString::fromUtf8( pszLocalName, localNameLen ) );
mFoundUnhandledGeometryElement = true;
}

if ( !mGeometryString.empty() )
isGeom = true;

if ( mDimension == 0 && isGeom )
{
Expand Down Expand Up @@ -807,6 +852,29 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
{
mParseModeStack.pop();
if ( mFoundUnhandledGeometryElement )
{
OGRGeometryH hGeom = OGR_G_CreateFromGML( mGeometryString.c_str() );
if ( hGeom )
{
const int wkbSize = OGR_G_WkbSize( hGeom );
unsigned char* pabyBuffer = new unsigned char[ wkbSize ];
#if GDAL_VERSION_MAJOR >= 2
OGR_G_ExportToIsoWkb( hGeom, wkbNDR, pabyBuffer );
#else
OGR_G_ExportToWkb( hGeom, wkbNDR, pabyBuffer );
#endif
QgsGeometry *g = new QgsGeometry();
g->fromWkb( pabyBuffer, wkbSize );
if ( mInvertAxisOrientation )
{
g->transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
}
mCurrentFeature->setGeometry( g );
OGR_G_DestroyGeometry( hGeom );
}
}
mGeometryString.clear();
}
else if ( theParseMode == boundingBox && isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
{
Expand Down Expand Up @@ -861,20 +929,19 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
memcmp( pszLocalName, mTypeNamePtr, mTypeName.size() ) == 0 ) )
{
Q_ASSERT( mCurrentFeature );
if ( mCurrentWKB.size() > 0 )
if ( !mCurrentFeature->geometry() )
{
QgsGeometry *g = new QgsGeometry();
g->fromWkb( mCurrentWKB, mCurrentWKB.size() );
mCurrentFeature->setGeometry( g );
mCurrentWKB = QgsWkbPtr( nullptr, 0 );
}
else if ( !mCurrentExtent.isEmpty() )
{
mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
}
else
{
mCurrentFeature->setGeometry( nullptr );
if ( mCurrentWKB.size() > 0 )
{
QgsGeometry *g = new QgsGeometry();
g->fromWkb( mCurrentWKB, mCurrentWKB.size() );
mCurrentFeature->setGeometry( g );
mCurrentWKB = QgsWkbPtr( nullptr, 0 );
}
else if ( !mCurrentExtent.isEmpty() )
{
mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
}
}
mCurrentFeature->setValid( true );

Expand Down Expand Up @@ -1033,6 +1100,14 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
mExceptionText = mStringCash;
mParseModeStack.pop();
}

if ( !mGeometryString.empty() )
{
mGeometryString.append( "</", 2 );
mGeometryString.append( pszLocalName, localNameLen );
mGeometryString.append( ">", 1 );
}

}

void QgsGmlStreamingParser::characters( const XML_Char* chars, int len )
Expand All @@ -1043,6 +1118,11 @@ void QgsGmlStreamingParser::characters( const XML_Char* chars, int len )
return;
}

if ( !mGeometryString.empty() )
{
mGeometryString.append( chars, len );
}

QgsGmlStreamingParser::ParseMode theParseMode = mParseModeStack.top();
if ( theParseMode == QgsGmlStreamingParser::attribute ||
theParseMode == QgsGmlStreamingParser::attributeTuple ||
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsgml.h
Expand Up @@ -34,6 +34,8 @@
#include <QStack>
#include <QVector>

#include <string>

/** This class builds features from GML data in a streaming way. The caller must call processData()
* as soon it has new content from the source. At any point, it can call
* getAndStealReadyFeatures() to collect the features that have been completely
Expand Down Expand Up @@ -296,6 +298,10 @@ class CORE_EXPORT QgsGmlStreamingParser
int mNumberReturned;
/** WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found */
int mNumberMatched;
/** XML blob containing geometry */
std::string mGeometryString;
/** Whether we found a unhandled geometry element */
bool mFoundUnhandledGeometryElement;
};


Expand Down
76 changes: 76 additions & 0 deletions tests/src/core/testqgsgml.cpp
Expand Up @@ -73,6 +73,8 @@ class TestQgsGML : public QObject
void testRenamedFields();
void testTruncatedResponse();
void testPartialFeature();
void testThroughOGRGeometry();
void testThroughOGRGeometry_urn_EPSG_4326();
};

const QString data1( "<myns:FeatureCollection "
Expand Down Expand Up @@ -1026,5 +1028,79 @@ void TestQgsGML::testPartialFeature()
QCOMPARE( features.size(), 0 );
}

void TestQgsGML::testThroughOGRGeometry()
{
QgsFields fields;
QgsGmlStreamingParser gmlParser( "mytypename", "mygeom", fields );
QCOMPARE( gmlParser.processData( QByteArray( "<myns:FeatureCollection "
"xmlns:myns='http://myns' "
"xmlns:gml='http://www.opengis.net/gml'>"
"<gml:featureMember>"
"<myns:mytypename fid='mytypename.1'>"
"<myns:mygeom>"
"<gml:CompositeSurface srsName='EPSG:27700'><gml:surfaceMember>"
"<gml:Polygon srsName='EPSG:27700'>"
"<gml:exterior>"
"<gml:LinearRing>"
"<gml:posList>0 0 0 10 10 10 10 0 0 0</gml:posList>"
"</gml:LinearRing>"
"</gml:exterior>"
"</gml:Polygon>"
"</gml:surfaceMember></gml:CompositeSurface>"
"</myns:mygeom>"
"</myns:mytypename>"
"</gml:featureMember>"
"</myns:FeatureCollection>" ), true ), true );
QCOMPARE( gmlParser.wkbType(), QGis::WKBPolygon );
QCOMPARE( gmlParser.getEPSGCode(), 27700 );
QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> features = gmlParser.getAndStealReadyFeatures();
QCOMPARE( features.size(), 1 );
QVERIFY( features[0].first->constGeometry() != nullptr );
QCOMPARE( features[0].first->constGeometry()->wkbType(), QGis::WKBMultiPolygon );
QgsMultiPolygon multi = features[0].first->constGeometry()->asMultiPolygon();
QCOMPARE( multi.size(), 1 );
QCOMPARE( multi[0].size(), 1 );
QCOMPARE( multi[0][0].size(), 5 );
delete features[0].first;
}

void TestQgsGML::testThroughOGRGeometry_urn_EPSG_4326()
{
QgsFields fields;
QgsGmlStreamingParser gmlParser( "mytypename", "mygeom", fields );
QCOMPARE( gmlParser.processData( QByteArray( "<myns:FeatureCollection "
"xmlns:myns='http://myns' "
"xmlns:gml='http://www.opengis.net/gml'>"
"<gml:featureMember>"
"<myns:mytypename fid='mytypename.1'>"
"<myns:mygeom>"
"<gml:CompositeSurface srsName='urn:ogc:def:crs:EPSG::4326'><gml:surfaceMember>"
"<gml:Polygon srsName='urn:ogc:def:crs:EPSG::4326'>"
"<gml:exterior>"
"<gml:LinearRing>"
"<gml:posList>49 2 49 3 59 3 49 2</gml:posList>"
"</gml:LinearRing>"
"</gml:exterior>"
"</gml:Polygon>"
"</gml:surfaceMember></gml:CompositeSurface>"
"</myns:mygeom>"
"</myns:mytypename>"
"</gml:featureMember>"
"</myns:FeatureCollection>" ), true ), true );
QCOMPARE( gmlParser.wkbType(), QGis::WKBPolygon );
QCOMPARE( gmlParser.getEPSGCode(), 4326 );
QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> features = gmlParser.getAndStealReadyFeatures();
QCOMPARE( features.size(), 1 );
QVERIFY( features[0].first->constGeometry() != nullptr );
QCOMPARE( features[0].first->constGeometry()->wkbType(), QGis::WKBMultiPolygon );
QgsMultiPolygon multi = features[0].first->constGeometry()->asMultiPolygon();
QCOMPARE( multi.size(), 1 );
QCOMPARE( multi[0].size(), 1 );
QCOMPARE( multi[0][0].size(), 4 );
QgsDebugMsg( multi[0][0][0].toString() );
QCOMPARE( multi[0][0][0], QgsPoint( 2, 49 ) );
delete features[0].first;
}

QTEST_MAIN( TestQgsGML )
#include "testqgsgml.moc"

0 comments on commit 690e554

Please sign in to comment.