Skip to content

Commit

Permalink
[FEATURE][QGIS Server] STARTINDEX param in WFS GetFeature Request
Browse files Browse the repository at this point in the history
STARTINDEX is standard in WFS 2.0, but it's an extension for WFS 1.0 implemented in QGIS Server.

STARTINDEX can be used to skip some features in the result set and in combination with MAXFEATURES provides for the ability to use WFS GetFeature to page through results. Note that STARTINDEX=0 means start with the first feature, skipping none.
  • Loading branch information
rldhont committed Dec 14, 2015
1 parent cf60ddb commit 30b3b68
Showing 1 changed file with 67 additions and 30 deletions.
97 changes: 67 additions & 30 deletions src/server/qgswfsserver.cpp
Expand Up @@ -411,6 +411,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format


long maxFeat = 0; long maxFeat = 0;
long maxFeatures = -1; long maxFeatures = -1;
long startIndex = 0;
long featureCounter = 0; long featureCounter = 0;
int layerPrec = 8; int layerPrec = 8;


Expand All @@ -426,6 +427,8 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format


if ( docElem.hasAttribute( "maxFeatures" ) ) if ( docElem.hasAttribute( "maxFeatures" ) )
maxFeatures = docElem.attribute( "maxFeatures" ).toLong(); maxFeatures = docElem.attribute( "maxFeatures" ).toLong();
if ( docElem.hasAttribute( "startIndex" ) )
startIndex = docElem.attribute( "startIndex" ).toLong();


QDomNodeList queryNodes = docElem.elementsByTagName( "Query" ); QDomNodeList queryNodes = docElem.elementsByTagName( "Query" );
QDomElement queryElem; QDomElement queryElem;
Expand Down Expand Up @@ -624,13 +627,16 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
req.setSubsetOfAttributes( attrIndexes ); req.setSubsetOfAttributes( attrIndexes );


QgsFeatureIterator fit = layer->getFeatures( req ); QgsFeatureIterator fit = layer->getFeatures( req );
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
++featCounter; {
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
} }
} }
Expand All @@ -643,7 +649,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
{ {
throw QgsMapServiceException( "RequestNotWellFormed", filter->parserErrorString() ); throw QgsMapServiceException( "RequestNotWellFormed", filter->parserErrorString() );
} }
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
expressionContext.setFeature( feature ); expressionContext.setFeature( feature );


Expand All @@ -654,26 +660,32 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
} }
if ( res.toInt() != 0 ) if ( res.toInt() != 0 )
{ {
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featureCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
{
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
++featCounter;
} }
} }
} }
} }
} }
else else
{ {
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
++featCounter; {
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
} }
} }
Expand All @@ -692,7 +704,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
QgsMessageLog::logMessage( mErrors.join( "\n" ) ); QgsMessageLog::logMessage( mErrors.join( "\n" ) );


QgsMapLayerRegistry::instance()->removeAllMapLayers(); QgsMapLayerRegistry::instance()->removeAllMapLayers();
if ( featureCounter == 0 ) if ( featureCounter <= startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );
endGetFeature( request, format ); endGetFeature( request, format );
return 0; return 0;
Expand Down Expand Up @@ -806,6 +818,15 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
maxFeat = mfString.toLong( &mfOk, 10 ); maxFeat = mfString.toLong( &mfOk, 10 );
} }


//read STARTINDEX
QMap<QString, QString>::const_iterator siIt = mParameters.find( "STARTINDEX" );
if ( siIt != mParameters.end() )
{
QString siString = siIt.value();
bool siOk;
startIndex = siString.toLong( &siOk, 10 );
}

//read PROPERTYNAME //read PROPERTYNAME
mWithGeom = true; mWithGeom = true;
mPropertyName = "*"; mPropertyName = "*";
Expand Down Expand Up @@ -959,7 +980,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
{ {
throw QgsMapServiceException( "RequestNotWellFormed", QString( "Expression filter error message: %1." ).arg( filter->parserErrorString() ) ); throw QgsMapServiceException( "RequestNotWellFormed", QString( "Expression filter error message: %1." ).arg( filter->parserErrorString() ) );
} }
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
expressionContext.setFeature( feature ); expressionContext.setFeature( feature );
QVariant res = filter->evaluate( &expressionContext ); QVariant res = filter->evaluate( &expressionContext );
Expand All @@ -969,11 +990,14 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
} }
if ( res.toInt() != 0 ) if ( res.toInt() != 0 )
{ {
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
++featCounter; {
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
} }
} }
Expand Down Expand Up @@ -1040,13 +1064,16 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
req.setSubsetOfAttributes( attrIndexes ); req.setSubsetOfAttributes( attrIndexes );


QgsFeatureIterator fit = layer->getFeatures( req ); QgsFeatureIterator fit = layer->getFeatures( req );
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
++featCounter; {
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
} }
} }
Expand Down Expand Up @@ -1078,7 +1105,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
} }
req.setSubsetOfAttributes( attrIndexes ); req.setSubsetOfAttributes( attrIndexes );
QgsFeatureIterator fit = layer->getFeatures( req ); QgsFeatureIterator fit = layer->getFeatures( req );
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
expressionContext.setFeature( feature ); expressionContext.setFeature( feature );
QVariant res = filter->evaluate( &expressionContext ); QVariant res = filter->evaluate( &expressionContext );
Expand All @@ -1088,12 +1115,15 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
} }
if ( res.toInt() != 0 ) if ( res.toInt() != 0 )
{ {
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featureCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
{
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
++featCounter;
} }
} }
} }
Expand Down Expand Up @@ -1121,14 +1151,17 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
} }
req.setSubsetOfAttributes( attrIndexes ); req.setSubsetOfAttributes( attrIndexes );
QgsFeatureIterator fit = layer->getFeatures( req ); QgsFeatureIterator fit = layer->getFeatures( req );
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat ) ) while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
{ {
mErrors << QString( "The feature %2 of layer for the TypeName '%1'" ).arg( tnStr ).arg( featureCounter ); mErrors << QString( "The feature %2 of layer for the TypeName '%1'" ).arg( tnStr ).arg( featureCounter );
if ( featureCounter == 0 ) if ( featureCounter == startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );


setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes ); if ( featureCounter >= startIndex )
++featCounter; {
setGetFeature( request, format, &feature, featCounter, layerPrec, layerCrs, attrIndexes, layerExcludedAttributes );
++featCounter;
}
++featureCounter; ++featureCounter;
} }
} }
Expand All @@ -1142,7 +1175,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
} }


QgsMapLayerRegistry::instance()->removeAllMapLayers(); QgsMapLayerRegistry::instance()->removeAllMapLayers();
if ( featureCounter == 0 ) if ( featureCounter <= startIndex )
startGetFeature( request, format, layerPrec, layerCrs, &searchRect ); startGetFeature( request, format, layerPrec, layerCrs, &searchRect );
endGetFeature( request, format ); endGetFeature( request, format );


Expand Down Expand Up @@ -1217,6 +1250,10 @@ void QgsWFSServer::startGetFeature( QgsRequestHandler& request, const QString& f
{ {
mapUrl.removeQueryItem( queryIt->first ); mapUrl.removeQueryItem( queryIt->first );
} }
else if ( queryIt->first.compare( "STARTINDEX", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "PROPERTYNAME", Qt::CaseInsensitive ) == 0 ) else if ( queryIt->first.compare( "PROPERTYNAME", Qt::CaseInsensitive ) == 0 )
{ {
mapUrl.removeQueryItem( queryIt->first ); mapUrl.removeQueryItem( queryIt->first );
Expand Down

3 comments on commit 30b3b68

@nyalldawson
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance of unit tests for this stuff?

@rldhont
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR #2583 will help me to create one.

@rldhont
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in this commit 9e8e868

Please sign in to comment.