Skip to content

Commit ddd37ad

Browse files
committedMay 30, 2016
Merge pull request #3138 from sbrunner/limit
Add and fix WFS server MAXFEATURES test
2 parents 3bdbf79 + 47cf924 commit ddd37ad

File tree

6 files changed

+143
-17
lines changed

6 files changed

+143
-17
lines changed
 

‎src/server/qgswfsserver.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,8 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
410410
mErrors = QStringList();
411411
mTypeNames = QStringList();
412412

413-
long maxFeat = 0;
414-
long maxFeatures = -1;
413+
long maxFeatures = 0;
414+
bool hasFeatureLimit = false;
415415
long startIndex = 0;
416416
long featureCounter = 0;
417417
int layerPrec = 8;
@@ -432,9 +432,14 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
432432
{
433433
QDomElement docElem = doc.documentElement();
434434
if ( docElem.hasAttribute( "maxFeatures" ) )
435+
{
436+
hasFeatureLimit = true;
435437
maxFeatures = docElem.attribute( "maxFeatures" ).toLong();
438+
}
436439
if ( docElem.hasAttribute( "startIndex" ) )
440+
{
437441
startIndex = docElem.attribute( "startIndex" ).toLong();
442+
}
438443

439444
QDomNodeList queryNodes = docElem.elementsByTagName( "Query" );
440445
QDomElement queryElem;
@@ -630,7 +635,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
630635
req.setSubsetOfAttributes( attrIndexes );
631636

632637
QgsFeatureIterator fit = layer->getFeatures( req );
633-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
638+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
634639
{
635640
if ( featureCounter == startIndex )
636641
startGetFeature( request, format, layerPrec, layerCrs, &searchRect );
@@ -652,7 +657,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
652657
{
653658
throw QgsMapServiceException( "RequestNotWellFormed", filter->parserErrorString() );
654659
}
655-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
660+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
656661
{
657662
expressionContext.setFeature( feature );
658663

@@ -679,7 +684,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
679684
}
680685
else
681686
{
682-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
687+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
683688
{
684689
if ( featureCounter == startIndex )
685690
startGetFeature( request, format, layerPrec, layerCrs, &searchRect );
@@ -819,8 +824,8 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
819824
{
820825
QString mfString = mfIt.value();
821826
bool mfOk;
827+
hasFeatureLimit = true;
822828
maxFeatures = mfString.toLong( &mfOk, 10 );
823-
maxFeat = mfString.toLong( &mfOk, 10 );
824829
}
825830

826831
//read STARTINDEX
@@ -983,7 +988,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
983988
{
984989
throw QgsMapServiceException( "RequestNotWellFormed", QString( "Expression filter error message: %1." ).arg( filter->parserErrorString() ) );
985990
}
986-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
991+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
987992
{
988993
expressionContext.setFeature( feature );
989994
QVariant res = filter->evaluate( &expressionContext );
@@ -1067,7 +1072,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
10671072
req.setSubsetOfAttributes( attrIndexes );
10681073

10691074
QgsFeatureIterator fit = layer->getFeatures( req );
1070-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
1075+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
10711076
{
10721077
if ( featureCounter == startIndex )
10731078
startGetFeature( request, format, layerPrec, layerCrs, &searchRect );
@@ -1108,7 +1113,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
11081113
}
11091114
req.setSubsetOfAttributes( attrIndexes );
11101115
QgsFeatureIterator fit = layer->getFeatures( req );
1111-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
1116+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
11121117
{
11131118
expressionContext.setFeature( feature );
11141119
QVariant res = filter->evaluate( &expressionContext );
@@ -1154,7 +1159,7 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
11541159
}
11551160
req.setSubsetOfAttributes( attrIndexes );
11561161
QgsFeatureIterator fit = layer->getFeatures( req );
1157-
while ( fit.nextFeature( feature ) && ( maxFeatures == -1 || featureCounter < maxFeat + startIndex ) )
1162+
while ( fit.nextFeature( feature ) && ( !hasFeatureLimit || featureCounter < maxFeatures + startIndex ) )
11581163
{
11591164
mErrors << QString( "The feature %2 of layer for the TypeName '%1'" ).arg( tnStr ).arg( featureCounter );
11601165
if ( featureCounter == startIndex )
@@ -1375,7 +1380,7 @@ void QgsWFSServer::endGetFeature( QgsRequestHandler& request, const QString& for
13751380
}
13761381
else
13771382
{
1378-
fcString = "</wfs:FeatureCollection>";
1383+
fcString = "</wfs:FeatureCollection>\n";
13791384
result = fcString.toUtf8();
13801385
request.endGetFeatureResponse( &result );
13811386
fcString = "";

‎tests/src/python/test_qgsserver.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,19 @@ def wfs_getfeature_compare(self, requestid, request):
282282

283283
query_string = 'MAP=%s&SERVICE=WFS&VERSION=1.0.0&REQUEST=%s' % (urllib.quote(project), request)
284284
header, body = [str(_v) for _v in self.server.handleRequest(query_string)]
285+
self.result_compare(
286+
'wfs_getfeature_' + requestid + '.txt',
287+
u"request %s failed.\n Query: %s" % (
288+
query_string,
289+
request,
290+
),
291+
header, body
292+
)
293+
294+
def result_compare(self, file_name, error_msg_header, header, body):
285295
self.assert_headers(header, body)
286296
response = header + body
287-
f = open(self.testdata_path + 'wfs_getfeature_' + requestid + '.txt')
297+
f = open(self.testdata_path + file_name)
288298
expected = f.read()
289299
f.close()
290300
# Store the output for debug or to regenerate the reference documents:
@@ -298,20 +308,64 @@ def wfs_getfeature_compare(self, requestid, request):
298308
"""
299309
response = re.sub(RE_STRIP_PATH, '', response)
300310
expected = re.sub(RE_STRIP_PATH, '', expected)
301-
self.assertEqual(response, expected, msg=u"request %s failed.\n Query: %s\n Expected:\n%s\n\n Response:\n%s"
302-
% (query_string,
303-
request,
311+
self.assertEqual(response, expected, msg=u"%s\n Expected:\n%s\n\n Response:\n%s"
312+
% (error_msg_header,
304313
unicode(expected, errors='replace'),
305314
unicode(response, errors='replace')))
306315

307316
def test_getfeature(self):
308317
tests = []
309318
tests.append(('nobbox', u'GetFeature&TYPENAME=testlayer'))
310319
tests.append(('startindex2', u'GetFeature&TYPENAME=testlayer&STARTINDEX=2'))
320+
tests.append(('limit2', u'GetFeature&TYPENAME=testlayer&MAXFEATURES=2'))
321+
tests.append(('start1_limit1', u'GetFeature&TYPENAME=testlayer&MAXFEATURES=1&STARTINDEX=1'))
311322

312323
for id, req in tests:
313324
self.wfs_getfeature_compare(id, req)
314325

326+
def wfs_getfeature_post_compare(self, requestid, request):
327+
project = self.testdata_path + "test+project_wfs.qgs"
328+
assert os.path.exists(project), "Project file not found: " + project
329+
330+
query_string = 'MAP={}'.format(urllib.quote(project))
331+
self.server.putenv("REQUEST_METHOD", "POST")
332+
self.server.putenv("REQUEST_BODY", request)
333+
header, body = self.server.handleRequest(query_string)
334+
self.server.putenv("REQUEST_METHOD", '')
335+
self.server.putenv("REQUEST_BODY", '')
336+
337+
self.result_compare(
338+
'wfs_getfeature_{}.txt'.format(requestid),
339+
"GetFeature in POST for '{}' failed.".format(requestid),
340+
header, body,
341+
)
342+
343+
def test_getfeature_post(self):
344+
template = """<?xml version="1.0" encoding="UTF-8"?>
345+
<wfs:GetFeature service="WFS" version="1.0.0" {} xmlns:wfs="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">
346+
<wfs:Query typeName="testlayer" xmlns:feature="http://www.qgis.org/gml">
347+
<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
348+
<ogc:BBOX>
349+
<ogc:PropertyName>geometry</ogc:PropertyName>
350+
<gml:Envelope xmlns:gml="http://www.opengis.net/gml">
351+
<gml:lowerCorner>8 44</gml:lowerCorner>
352+
<gml:upperCorner>9 45</gml:upperCorner>
353+
</gml:Envelope>
354+
</ogc:BBOX>
355+
</ogc:Filter>
356+
</wfs:Query>
357+
</wfs:GetFeature>
358+
"""
359+
360+
tests = []
361+
tests.append(('nobbox', template.format("")))
362+
tests.append(('startindex2', template.format('startIndex="2"')))
363+
tests.append(('limit2', template.format('maxFeatures="2"')))
364+
tests.append(('start1_limit1', template.format('startIndex="1" maxFeatures="1"')))
365+
366+
for id, req in tests:
367+
self.wfs_getfeature_post_compare(id, req)
368+
315369
def test_getLegendGraphics(self):
316370
"""Test that does not return an exception but an image"""
317371
parms = {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Content-Type: text/xml; charset=utf-8
2+
3+
<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml" xmlns:ows="http://www.opengis.net/ows" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:qgs="http://www.qgis.org/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd http://www.qgis.org/gml http:?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=testlayer&amp;OUTPUTFORMAT=XMLSCHEMA"><gml:boundedBy>
4+
<gml:Box srsName="EPSG:4326">
5+
<gml:coordinates cs="," ts=" ">8.2034593,44.90139483 8.203547,44.90148254</gml:coordinates>
6+
</gml:Box>
7+
</gml:boundedBy>
8+
<gml:featureMember>
9+
<qgs:testlayer fid="testlayer.0">
10+
<gml:boundedBy>
11+
<gml:Box srsName="EPSG:4326">
12+
<gml:coordinates cs="," ts=" ">8.20349634,44.90148253 8.20349634,44.90148253</gml:coordinates>
13+
</gml:Box>
14+
</gml:boundedBy>
15+
<qgs:geometry>
16+
<gml:Point srsName="EPSG:4326">
17+
<gml:coordinates cs="," ts=" ">8.20349634,44.90148253</gml:coordinates>
18+
</gml:Point>
19+
</qgs:geometry>
20+
<qgs:id>1</qgs:id>
21+
<qgs:name>one</qgs:name>
22+
<qgs:utf8nameè>one èé</qgs:utf8nameè>
23+
</qgs:testlayer>
24+
</gml:featureMember>
25+
<gml:featureMember>
26+
<qgs:testlayer fid="testlayer.1">
27+
<gml:boundedBy>
28+
<gml:Box srsName="EPSG:4326">
29+
<gml:coordinates cs="," ts=" ">8.20354699,44.90143568 8.20354699,44.90143568</gml:coordinates>
30+
</gml:Box>
31+
</gml:boundedBy>
32+
<qgs:geometry>
33+
<gml:Point srsName="EPSG:4326">
34+
<gml:coordinates cs="," ts=" ">8.20354699,44.90143568</gml:coordinates>
35+
</gml:Point>
36+
</qgs:geometry>
37+
<qgs:id>2</qgs:id>
38+
<qgs:name>two</qgs:name>
39+
<qgs:utf8nameè>two àò</qgs:utf8nameè>
40+
</qgs:testlayer>
41+
</gml:featureMember>
42+
</wfs:FeatureCollection>

‎tests/testdata/qgis_server/wfs_getfeature_nobbox.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,4 @@ Content-Type: text/xml; charset=utf-8
5656
<qgs:utf8nameè>three èé↓</qgs:utf8nameè>
5757
</qgs:testlayer>
5858
</gml:featureMember>
59-
</wfs:FeatureCollection>
59+
</wfs:FeatureCollection>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Content-Type: text/xml; charset=utf-8
2+
3+
<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml" xmlns:ows="http://www.opengis.net/ows" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:qgs="http://www.qgis.org/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd http://www.qgis.org/gml http:?SERVICE=WFS&amp;VERSION=1.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=testlayer&amp;OUTPUTFORMAT=XMLSCHEMA"><gml:boundedBy>
4+
<gml:Box srsName="EPSG:4326">
5+
<gml:coordinates cs="," ts=" ">8.2034593,44.90139483 8.203547,44.90148254</gml:coordinates>
6+
</gml:Box>
7+
</gml:boundedBy>
8+
<gml:featureMember>
9+
<qgs:testlayer fid="testlayer.1">
10+
<gml:boundedBy>
11+
<gml:Box srsName="EPSG:4326">
12+
<gml:coordinates cs="," ts=" ">8.20354699,44.90143568 8.20354699,44.90143568</gml:coordinates>
13+
</gml:Box>
14+
</gml:boundedBy>
15+
<qgs:geometry>
16+
<gml:Point srsName="EPSG:4326">
17+
<gml:coordinates cs="," ts=" ">8.20354699,44.90143568</gml:coordinates>
18+
</gml:Point>
19+
</qgs:geometry>
20+
<qgs:id>2</qgs:id>
21+
<qgs:name>two</qgs:name>
22+
<qgs:utf8nameè>two àò</qgs:utf8nameè>
23+
</qgs:testlayer>
24+
</gml:featureMember>
25+
</wfs:FeatureCollection>

‎tests/testdata/qgis_server/wfs_getfeature_startindex2.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ Content-Type: text/xml; charset=utf-8
2222
<qgs:utf8nameè>three èé↓</qgs:utf8nameè>
2323
</qgs:testlayer>
2424
</gml:featureMember>
25-
</wfs:FeatureCollection>
25+
</wfs:FeatureCollection>

0 commit comments

Comments
 (0)
Please sign in to comment.