Skip to content

Commit 7ed1a7f

Browse files
committed
Merge branch 'master' of github.com:qgis/QGIS
2 parents 5839a3a + 7a3a38b commit 7ed1a7f

13 files changed

+490
-77
lines changed

src/core/qgsgml.cpp

Lines changed: 95 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++ )
@@ -411,6 +415,8 @@ QgsGmlStreamingParser::~QgsGmlStreamingParser()
411415
{
412416
delete featPair.first;
413417
}
418+
419+
delete mCurrentFeature;
414420
}
415421

416422
bool QgsGmlStreamingParser::processData( const QByteArray& data, bool atEnd )
@@ -476,6 +482,24 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
476482

477483
const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
478484
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+
479503
if ( isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
480504
{
481505
mParseModeStack.push( coordinate );
@@ -513,6 +537,8 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
513537
memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
514538
{
515539
mParseModeStack.push( QgsGmlStreamingParser::geometry );
540+
mFoundUnhandledGeometryElement = false;
541+
mGeometryString.clear();
516542
}
517543
//else if ( mParseModeStack.size() == 0 && elementName == mGMLNameSpaceURI + NS_SEPARATOR + "boundedBy" )
518544
else if ( isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
@@ -737,6 +763,27 @@ void QgsGmlStreamingParser::startElement( const XML_Char* el, const XML_Char** a
737763
// e.g: http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=cp:CadastralParcel
738764
mTruncatedResponse = true;
739765
}
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;
740787

741788
if ( mDimension == 0 && isGeom )
742789
{
@@ -805,6 +852,29 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
805852
memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
806853
{
807854
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();
808878
}
809879
else if ( theParseMode == boundingBox && isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
810880
{
@@ -859,20 +929,19 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
859929
memcmp( pszLocalName, mTypeNamePtr, mTypeName.size() ) == 0 ) )
860930
{
861931
Q_ASSERT( mCurrentFeature );
862-
if ( mCurrentWKB.size() > 0 )
932+
if ( !mCurrentFeature->geometry() )
863933
{
864-
QgsGeometry *g = new QgsGeometry();
865-
g->fromWkb( mCurrentWKB, mCurrentWKB.size() );
866-
mCurrentFeature->setGeometry( g );
867-
mCurrentWKB = QgsWkbPtr( nullptr, 0 );
868-
}
869-
else if ( !mCurrentExtent.isEmpty() )
870-
{
871-
mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
872-
}
873-
else
874-
{
875-
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+
}
876945
}
877946
mCurrentFeature->setValid( true );
878947

@@ -1031,6 +1100,14 @@ void QgsGmlStreamingParser::endElement( const XML_Char* el )
10311100
mExceptionText = mStringCash;
10321101
mParseModeStack.pop();
10331102
}
1103+
1104+
if ( !mGeometryString.empty() )
1105+
{
1106+
mGeometryString.append( "</", 2 );
1107+
mGeometryString.append( pszLocalName, localNameLen );
1108+
mGeometryString.append( ">", 1 );
1109+
}
1110+
10341111
}
10351112

10361113
void QgsGmlStreamingParser::characters( const XML_Char* chars, int len )
@@ -1041,6 +1118,11 @@ void QgsGmlStreamingParser::characters( const XML_Char* chars, int len )
10411118
return;
10421119
}
10431120

1121+
if ( !mGeometryString.empty() )
1122+
{
1123+
mGeometryString.append( chars, len );
1124+
}
1125+
10441126
QgsGmlStreamingParser::ParseMode theParseMode = mParseModeStack.top();
10451127
if ( theParseMode == QgsGmlStreamingParser::attribute ||
10461128
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

src/core/qgslogger.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void QgsLogger::debug( const QString& msg, int debuglevel, const char* file, con
9595
}
9696

9797
#ifndef _MSC_VER
98-
m.prepend( file + sPrefixLength );
98+
m.prepend( file + ( file[0] == '/' ? sPrefixLength : 0 ) );
9999
#else
100100
m.prepend( file );
101101
#endif

src/providers/wfs/qgswfscapabilities.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ void QgsWFSCapabilities::Capabilities::clear()
7272
setAllTypenames.clear();
7373
mapUnprefixedTypenameToPrefixedTypename.clear();
7474
setAmbiguousUnprefixedTypename.clear();
75+
useEPSGColumnFormat = false;
7576
}
7677

7778
QString QgsWFSCapabilities::Capabilities::addPrefixIfNeeded( const QString& name ) const
@@ -283,7 +284,11 @@ void QgsWFSCapabilities::capabilitiesReplyFinished()
283284
defaultCRSList = featureTypeElem.elementsByTagName( "DefaultCRS" );
284285
if ( defaultCRSList.length() > 0 )
285286
{
286-
featureType.crslist.append( NormalizeSRSName( defaultCRSList.at( 0 ).toElement().text() ) );
287+
QString srsname( defaultCRSList.at( 0 ).toElement().text() );
288+
// Some servers like Geomedia advertize EPSG:XXXX even in WFS 1.1 or 2.0
289+
if ( srsname.startsWith( "EPSG:" ) )
290+
mCaps.useEPSGColumnFormat = true;
291+
featureType.crslist.append( NormalizeSRSName( srsname ) );
287292
}
288293

289294
//OtherSRS

src/providers/wfs/qgswfscapabilities.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class QgsWFSCapabilities : public QgsWFSRequest
3535
//! description of a vector layer
3636
struct FeatureType
3737
{
38+
//! Default constructor
39+
FeatureType() : insertCap( false ), updateCap( false ), deleteCap( false ) {}
40+
3841
QString name;
3942
QString title;
4043
QString abstract;
@@ -92,6 +95,7 @@ class QgsWFSCapabilities : public QgsWFSRequest
9295
QList<FeatureType> featureTypes;
9396
QList<Function> spatialPredicatesList;
9497
QList<Function> functionList;
98+
bool useEPSGColumnFormat; // whether to use EPSG:XXXX srsname
9599

96100
QSet< QString > setAllTypenames;
97101
QMap< QString, QString> mapUnprefixedTypenameToPrefixedTypename;

src/providers/wfs/qgswfsfeatureiterator.cpp

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,15 @@ QUrl QgsWFSFeatureDownloader::buildURL( int startIndex, int maxFeatures, bool fo
302302
{
303303
invertAxis = !invertAxis;
304304
}
305-
getFeatureUrl.addQueryItem( "BBOX", QString(( invertAxis ) ? "%2,%1,%4,%3" : "%1,%2,%3,%4" )
306-
.arg( qgsDoubleToString( mShared->mRect.xMinimum() ),
307-
qgsDoubleToString( mShared->mRect.yMinimum() ),
308-
qgsDoubleToString( mShared->mRect.xMaximum() ),
309-
qgsDoubleToString( mShared->mRect.yMaximum() ) ) );
305+
QString bbox( QString(( invertAxis ) ? "%2,%1,%4,%3" : "%1,%2,%3,%4" )
306+
.arg( qgsDoubleToString( mShared->mRect.xMinimum() ),
307+
qgsDoubleToString( mShared->mRect.yMinimum() ),
308+
qgsDoubleToString( mShared->mRect.xMaximum() ),
309+
qgsDoubleToString( mShared->mRect.yMaximum() ) ) );
310+
// Some servers like Geomedia need the srsname to be explictly appended
311+
// otherwise they are confused and do not interpret it properly
312+
bbox += "," + mShared->srsName();
313+
getFeatureUrl.addQueryItem( "BBOX", bbox );
310314
}
311315
else if ( !mShared->mWFSFilter.isEmpty() )
312316
{
@@ -395,6 +399,9 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
395399
const int maxRetry = s.value( "/qgis/defaultTileMaxRetry", "3" ).toInt();
396400
int retryIter = 0;
397401
int lastValidTotalDownloadedFeatureCount = 0;
402+
int pagingIter = 1;
403+
QString gmlIdFirstFeatureFirstIter;
404+
bool disablePaging = false;
398405
while ( true )
399406
{
400407
success = true;
@@ -414,7 +421,7 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
414421
true, /* forceRefresh */
415422
false /* cache */ );
416423

417-
int featureCount = 0;
424+
int featureCountForThisResponse = 0;
418425
while ( true )
419426
{
420427
loop.exec( QEventLoop::ExcludeUserInputEvents );
@@ -524,7 +531,6 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
524531
QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> featurePtrList =
525532
parser->getAndStealReadyFeatures();
526533

527-
featureCount += featurePtrList.size();
528534
mTotalDownloadedFeatureCount += featurePtrList.size();
529535

530536
if ( !mStop )
@@ -538,7 +544,29 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
538544
for ( int i = 0;i < featurePtrList.size();i++ )
539545
{
540546
QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair& featPair = featurePtrList[i];
541-
featureList.push_back( QgsWFSFeatureGmlIdPair( *( featPair.first ), featPair.second ) );
547+
const QgsFeature& f = *( featPair.first );
548+
QString gmlId( featPair.second );
549+
if ( gmlId.isEmpty() )
550+
{
551+
// Should normally not happen on sane WFS sources, but can happen with
552+
// Geomedia
553+
gmlId = QgsWFSUtils::getMD5( f );
554+
if ( !mShared->mHasWarnedAboutMissingFeatureId )
555+
{
556+
QgsDebugMsg( "Server returns features without fid/gml:id. Computing a fake one using feature attributes" );
557+
mShared->mHasWarnedAboutMissingFeatureId = true;
558+
}
559+
}
560+
if ( pagingIter == 1 && featureCountForThisResponse == 0 )
561+
{
562+
gmlIdFirstFeatureFirstIter = gmlId;
563+
}
564+
else if ( pagingIter == 2 && featureCountForThisResponse == 0 && gmlIdFirstFeatureFirstIter == gmlId )
565+
{
566+
disablePaging = true;
567+
QgsDebugMsg( "Server does not seem to properly support paging since it returned the same first feature for 2 different page requests. Disabling paging" );
568+
}
569+
featureList.push_back( QgsWFSFeatureGmlIdPair( f, gmlId ) );
542570
delete featPair.first;
543571
if (( i > 0 && ( i % 1000 ) == 0 ) || i + 1 == featurePtrList.size() )
544572
{
@@ -557,6 +585,8 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
557585

558586
featureList.clear();
559587
}
588+
589+
featureCountForThisResponse ++;
560590
}
561591
}
562592

@@ -580,7 +610,7 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
580610
if ( ++retryIter <= maxRetry )
581611
{
582612
QgsMessageLog::logMessage( tr( "Retrying request %1: %2/%3" ).arg( url.toString() ).arg( retryIter ).arg( maxRetry ), tr( "WFS" ) );
583-
featureCount = 0;
613+
featureCountForThisResponse = 0;
584614
mTotalDownloadedFeatureCount = lastValidTotalDownloadedFeatureCount;
585615
continue;
586616
}
@@ -596,8 +626,18 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
596626
if ( maxFeatures == 1 )
597627
break;
598628
// Detect if we are at the last page
599-
if (( mShared->mMaxFeatures > 0 && featureCount < mShared->mMaxFeatures ) || featureCount == 0 )
629+
if (( mShared->mMaxFeatures > 0 && featureCountForThisResponse < mShared->mMaxFeatures ) || featureCountForThisResponse == 0 )
600630
break;
631+
++ pagingIter;
632+
if ( disablePaging )
633+
{
634+
mSupportsPaging = mShared->mCaps.supportsPaging = false;
635+
mTotalDownloadedFeatureCount = 0;
636+
if ( mShared->mMaxFeaturesWasSetFromDefaultForPaging )
637+
{
638+
mShared->mMaxFeatures = 0;
639+
}
640+
}
601641
}
602642

603643
mStop = true;

0 commit comments

Comments
 (0)