Skip to content

Commit 690e554

Browse files
committed
QgsGML: handle more type of geometries (Arc, CompositeSurface, etc...) through OGR
1 parent a4f1511 commit 690e554

File tree

3 files changed

+175
-13
lines changed

3 files changed

+175
-13
lines changed

src/core/qgsgml.cpp

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include <QSettings>
3333
#include <QUrl>
3434

35+
#include "ogr_api.h"
36+
3537
#include <limits>
3638

3739
static const char NS_SEPARATOR = '?';
@@ -288,6 +290,7 @@ QgsGmlStreamingParser::QgsGmlStreamingParser( const QString& typeName,
288290
, mInvertAxisOrientation( invertAxisOrientation )
289291
, mNumberReturned( -1 )
290292
, mNumberMatched( -1 )
293+
, mFoundUnhandledGeometryElement( false )
291294
{
292295
mThematicAttributes.clear();
293296
for ( int i = 0; i < fields.size(); i++ )
@@ -348,6 +351,7 @@ QgsGmlStreamingParser::QgsGmlStreamingParser( const QList<LayerProperties>& laye
348351
, mInvertAxisOrientation( invertAxisOrientation )
349352
, mNumberReturned( -1 )
350353
, mNumberMatched( -1 )
354+
, mFoundUnhandledGeometryElement( false )
351355
{
352356
mThematicAttributes.clear();
353357
for ( int i = 0; i < fields.size(); i++ )
@@ -478,6 +482,24 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
478482

479483
const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
480484
bool isGeom = false;
485+
486+
if ( theParseMode == geometry || theParseMode == coordinate || theParseMode == posList ||
487+
theParseMode == multiPoint || theParseMode == multiLine || theParseMode == multiPolygon )
488+
{
489+
mGeometryString.append( "<", 1 );
490+
mGeometryString.append( pszLocalName, localNameLen );
491+
mGeometryString.append( " ", 1 );
492+
for ( const XML_Char** attrIter = attr; attrIter && *attrIter; attrIter += 2 )
493+
{
494+
mGeometryString.append( attrIter[0] );
495+
mGeometryString.append( "=\"", 2 );
496+
mGeometryString.append( attrIter[1] );
497+
mGeometryString.append( "\" ", 2 );
498+
499+
}
500+
mGeometryString.append( ">", 1 );
501+
}
502+
481503
if ( isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
482504
{
483505
mParseModeStack.push( coordinate );
@@ -515,6 +537,8 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
515537
memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
516538
{
517539
mParseModeStack.push( QgsGmlStreamingParser::geometry );
540+
mFoundUnhandledGeometryElement = false;
541+
mGeometryString.clear();
518542
}
519543
//else if ( mParseModeStack.size() == 0 && elementName == mGMLNameSpaceURI + NS_SEPARATOR + "boundedBy" )
520544
else if ( isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
@@ -739,6 +763,27 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
739763
// e.g: http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=cp:CadastralParcel
740764
mTruncatedResponse = true;
741765
}
766+
else if ( !mGeometryString.empty() &&
767+
!LOCALNAME_EQUALS( "exterior" ) &&
768+
!LOCALNAME_EQUALS( "interior" ) &&
769+
!LOCALNAME_EQUALS( "innerBoundaryIs" ) &&
770+
!LOCALNAME_EQUALS( "outerBoundaryIs" ) &&
771+
!LOCALNAME_EQUALS( "LinearRing" ) &&
772+
!LOCALNAME_EQUALS( "pointMember" ) &&
773+
!LOCALNAME_EQUALS( "curveMember" ) &&
774+
!LOCALNAME_EQUALS( "lineStringMember" ) &&
775+
!LOCALNAME_EQUALS( "polygonMember" ) &&
776+
!LOCALNAME_EQUALS( "surfaceMember" ) &&
777+
!LOCALNAME_EQUALS( "Curve" ) &&
778+
!LOCALNAME_EQUALS( "segments" ) &&
779+
!LOCALNAME_EQUALS( "LineStringSegment" ) )
780+
{
781+
//QgsDebugMsg( "Found unhandled geometry element " + QString::fromUtf8( pszLocalName, localNameLen ) );
782+
mFoundUnhandledGeometryElement = true;
783+
}
784+
785+
if ( !mGeometryString.empty() )
786+
isGeom = true;
742787

743788
if ( mDimension == 0 && isGeom )
744789
{
@@ -807,6 +852,29 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
807852
memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
808853
{
809854
mParseModeStack.pop();
855+
if ( mFoundUnhandledGeometryElement )
856+
{
857+
OGRGeometryH hGeom = OGR_G_CreateFromGML( mGeometryString.c_str() );
858+
if ( hGeom )
859+
{
860+
const int wkbSize = OGR_G_WkbSize( hGeom );
861+
unsigned char* pabyBuffer = new unsigned char[ wkbSize ];
862+
#if GDAL_VERSION_MAJOR >= 2
863+
OGR_G_ExportToIsoWkb( hGeom, wkbNDR, pabyBuffer );
864+
#else
865+
OGR_G_ExportToWkb( hGeom, wkbNDR, pabyBuffer );
866+
#endif
867+
QgsGeometry *g = new QgsGeometry();
868+
g->fromWkb( pabyBuffer, wkbSize );
869+
if ( mInvertAxisOrientation )
870+
{
871+
g->transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
872+
}
873+
mCurrentFeature->setGeometry( g );
874+
OGR_G_DestroyGeometry( hGeom );
875+
}
876+
}
877+
mGeometryString.clear();
810878
}
811879
else if ( theParseMode == boundingBox && isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
812880
{
@@ -861,20 +929,19 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
861929
memcmp( pszLocalName, mTypeNamePtr, mTypeName.size() ) == 0 ) )
862930
{
863931
Q_ASSERT( mCurrentFeature );
864-
if ( mCurrentWKB.size() > 0 )
932+
if ( !mCurrentFeature->geometry() )
865933
{
866-
QgsGeometry *g = new QgsGeometry();
867-
g->fromWkb( mCurrentWKB, mCurrentWKB.size() );
868-
mCurrentFeature->setGeometry( g );
869-
mCurrentWKB = QgsWkbPtr( nullptr, 0 );
870-
}
871-
else if ( !mCurrentExtent.isEmpty() )
872-
{
873-
mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
874-
}
875-
else
876-
{
877-
mCurrentFeature->setGeometry( nullptr );
934+
if ( mCurrentWKB.size() > 0 )
935+
{
936+
QgsGeometry *g = new QgsGeometry();
937+
g->fromWkb( mCurrentWKB, mCurrentWKB.size() );
938+
mCurrentFeature->setGeometry( g );
939+
mCurrentWKB = QgsWkbPtr( nullptr, 0 );
940+
}
941+
else if ( !mCurrentExtent.isEmpty() )
942+
{
943+
mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
944+
}
878945
}
879946
mCurrentFeature->setValid( true );
880947

@@ -1033,6 +1100,14 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
10331100
mExceptionText = mStringCash;
10341101
mParseModeStack.pop();
10351102
}
1103+
1104+
if ( !mGeometryString.empty() )
1105+
{
1106+
mGeometryString.append( "</", 2 );
1107+
mGeometryString.append( pszLocalName, localNameLen );
1108+
mGeometryString.append( ">", 1 );
1109+
}
1110+
10361111
}
10371112

10381113
void QgsGmlStreamingParser::characters( const XML_Char* chars, int len )
@@ -1043,6 +1118,11 @@ void QgsGmlStreamingParser::characters( const XML_Char* chars, int len )
10431118
return;
10441119
}
10451120

1121+
if ( !mGeometryString.empty() )
1122+
{
1123+
mGeometryString.append( chars, len );
1124+
}
1125+
10461126
QgsGmlStreamingParser::ParseMode theParseMode = mParseModeStack.top();
10471127
if ( theParseMode == QgsGmlStreamingParser::attribute ||
10481128
theParseMode == QgsGmlStreamingParser::attributeTuple ||

src/core/qgsgml.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#include <QStack>
3535
#include <QVector>
3636

37+
#include <string>
38+
3739
/** This class builds features from GML data in a streaming way. The caller must call processData()
3840
* as soon it has new content from the source. At any point, it can call
3941
* getAndStealReadyFeatures() to collect the features that have been completely
@@ -296,6 +298,10 @@ class CORE_EXPORT QgsGmlStreamingParser
296298
int mNumberReturned;
297299
/** WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found */
298300
int mNumberMatched;
301+
/** XML blob containing geometry */
302+
std::string mGeometryString;
303+
/** Whether we found a unhandled geometry element */
304+
bool mFoundUnhandledGeometryElement;
299305
};
300306

301307

tests/src/core/testqgsgml.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class TestQgsGML : public QObject
7373
void testRenamedFields();
7474
void testTruncatedResponse();
7575
void testPartialFeature();
76+
void testThroughOGRGeometry();
77+
void testThroughOGRGeometry_urn_EPSG_4326();
7678
};
7779

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

1031+
void TestQgsGML::testThroughOGRGeometry()
1032+
{
1033+
QgsFields fields;
1034+
QgsGmlStreamingParser gmlParser( "mytypename", "mygeom", fields );
1035+
QCOMPARE( gmlParser.processData( QByteArray( "<myns:FeatureCollection "
1036+
"xmlns:myns='http://myns' "
1037+
"xmlns:gml='http://www.opengis.net/gml'>"
1038+
"<gml:featureMember>"
1039+
"<myns:mytypename fid='mytypename.1'>"
1040+
"<myns:mygeom>"
1041+
"<gml:CompositeSurface srsName='EPSG:27700'><gml:surfaceMember>"
1042+
"<gml:Polygon srsName='EPSG:27700'>"
1043+
"<gml:exterior>"
1044+
"<gml:LinearRing>"
1045+
"<gml:posList>0 0 0 10 10 10 10 0 0 0</gml:posList>"
1046+
"</gml:LinearRing>"
1047+
"</gml:exterior>"
1048+
"</gml:Polygon>"
1049+
"</gml:surfaceMember></gml:CompositeSurface>"
1050+
"</myns:mygeom>"
1051+
"</myns:mytypename>"
1052+
"</gml:featureMember>"
1053+
"</myns:FeatureCollection>" ), true ), true );
1054+
QCOMPARE( gmlParser.wkbType(), QGis::WKBPolygon );
1055+
QCOMPARE( gmlParser.getEPSGCode(), 27700 );
1056+
QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> features = gmlParser.getAndStealReadyFeatures();
1057+
QCOMPARE( features.size(), 1 );
1058+
QVERIFY( features[0].first->constGeometry() != nullptr );
1059+
QCOMPARE( features[0].first->constGeometry()->wkbType(), QGis::WKBMultiPolygon );
1060+
QgsMultiPolygon multi = features[0].first->constGeometry()->asMultiPolygon();
1061+
QCOMPARE( multi.size(), 1 );
1062+
QCOMPARE( multi[0].size(), 1 );
1063+
QCOMPARE( multi[0][0].size(), 5 );
1064+
delete features[0].first;
1065+
}
1066+
1067+
void TestQgsGML::testThroughOGRGeometry_urn_EPSG_4326()
1068+
{
1069+
QgsFields fields;
1070+
QgsGmlStreamingParser gmlParser( "mytypename", "mygeom", fields );
1071+
QCOMPARE( gmlParser.processData( QByteArray( "<myns:FeatureCollection "
1072+
"xmlns:myns='http://myns' "
1073+
"xmlns:gml='http://www.opengis.net/gml'>"
1074+
"<gml:featureMember>"
1075+
"<myns:mytypename fid='mytypename.1'>"
1076+
"<myns:mygeom>"
1077+
"<gml:CompositeSurface srsName='urn:ogc:def:crs:EPSG::4326'><gml:surfaceMember>"
1078+
"<gml:Polygon srsName='urn:ogc:def:crs:EPSG::4326'>"
1079+
"<gml:exterior>"
1080+
"<gml:LinearRing>"
1081+
"<gml:posList>49 2 49 3 59 3 49 2</gml:posList>"
1082+
"</gml:LinearRing>"
1083+
"</gml:exterior>"
1084+
"</gml:Polygon>"
1085+
"</gml:surfaceMember></gml:CompositeSurface>"
1086+
"</myns:mygeom>"
1087+
"</myns:mytypename>"
1088+
"</gml:featureMember>"
1089+
"</myns:FeatureCollection>" ), true ), true );
1090+
QCOMPARE( gmlParser.wkbType(), QGis::WKBPolygon );
1091+
QCOMPARE( gmlParser.getEPSGCode(), 4326 );
1092+
QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> features = gmlParser.getAndStealReadyFeatures();
1093+
QCOMPARE( features.size(), 1 );
1094+
QVERIFY( features[0].first->constGeometry() != nullptr );
1095+
QCOMPARE( features[0].first->constGeometry()->wkbType(), QGis::WKBMultiPolygon );
1096+
QgsMultiPolygon multi = features[0].first->constGeometry()->asMultiPolygon();
1097+
QCOMPARE( multi.size(), 1 );
1098+
QCOMPARE( multi[0].size(), 1 );
1099+
QCOMPARE( multi[0][0].size(), 4 );
1100+
QgsDebugMsg( multi[0][0][0].toString() );
1101+
QCOMPARE( multi[0][0][0], QgsPoint( 2, 49 ) );
1102+
delete features[0].first;
1103+
}
1104+
10291105
QTEST_MAIN( TestQgsGML )
10301106
#include "testqgsgml.moc"

0 commit comments

Comments
 (0)