Skip to content

Commit

Permalink
Add shapefile provider tests
Browse files Browse the repository at this point in the history
+ Fix OGR provider so it does not return features without geometry when
filtering with a FilterRect
  • Loading branch information
m-kuhn committed May 22, 2015
1 parent e7b7549 commit afc3996
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 26 deletions.
15 changes: 6 additions & 9 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,9 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
// spatial query to select features
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
OGRGeometryH filter = 0;
QString wktExtent = QString( "POLYGON((%1))" ).arg( mRequest.filterRect().asPolygon() );
QByteArray ba = wktExtent.toAscii();
const char *wktText = ba;

OGR_G_CreateFromWkt(( char ** )&wktText, NULL, &filter );
QgsDebugMsg( "Setting spatial filter using " + wktExtent );
OGR_L_SetSpatialFilter( ogrLayer, filter );
OGR_G_DestroyGeometry( filter );
const QgsRectangle& rect = mRequest.filterRect();

OGR_L_SetSpatialFilterRect( ogrLayer, rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum() );
}
else
{
Expand Down Expand Up @@ -186,6 +180,9 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature )
if ( !readFeature( fet, feature ) )
continue;

if ( mRequest.filterType() == QgsFeatureRequest::FilterRect && !feature.constGeometry() )
continue;

// we have a feature, end this cycle
feature.setValid( true );
OGR_F_Destroy( fet );
Expand Down
3 changes: 3 additions & 0 deletions src/providers/spatialite/qgsspatialitefeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ bool QgsSpatiaLiteFeatureIterator::close()

bool QgsSpatiaLiteFeatureIterator::prepareStatement( QString whereClause )
{
if ( !mHandle )
return false;

try
{
QString sql = QString( "SELECT %1" ).arg( mHasPrimaryKey ? quotedPrimaryKey() : "0" );
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ ADD_PYTHON_TEST(PyQgsEditWidgets test_qgseditwidgets.py)
ADD_PYTHON_TEST(PyQgsRangeWidgets test_qgsrangewidgets.py)
ADD_PYTHON_TEST(PyQgsPostgresProvider test_provider_postgres.py)
ADD_PYTHON_TEST(PyQgsSpatialiteProvider test_provider_spatialite.py)
ADD_PYTHON_TEST(PyQgsShapefileProvider test_provider_shapefile.py)
ADD_PYTHON_TEST(PyQgsSpatialiteProviderOther test_qgsspatialiteprovider.py)
IF (WITH_APIDOC)
ADD_PYTHON_TEST(PyQgsDocCoverage test_qgsdoccoverage.py)
Expand Down
40 changes: 23 additions & 17 deletions tests/src/python/providertestbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@
from qgis.core import QgsRectangle, QgsFeatureRequest, QgsGeometry, NULL
from utilities import TestCase

class ProviderTestCase(object):

class ProviderTestCase(object):
def runGetFeatureTests(self, provider):
assert len( [f for f in provider.getFeatures()] ) == 5
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression( 'name IS NOT NULL' ) )] ) == 4
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('name LIKE \'Apple\'' ) )] ) == 1
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('name ILIKE \'aPple\'' ) )] ) == 1
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('name ILIKE \'%pp%\'' ) )] ) == 1
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('cnt > 0' ) ) ] ) == 4
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('cnt < 0' ) ) ] ) == 1
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('cnt >= 100' ) ) ] ) == 4
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('cnt <= 100' ) ) ] ) == 2
assert len( [f for f in provider.getFeatures( QgsFeatureRequest().setFilterExpression('pk IN (1, 2, 4, 8)' ) )] ) == 3
assert len([f for f in provider.getFeatures()]) == 5
assert len([f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('name IS NOT NULL'))]) == 4
assert len(
[f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('name LIKE \'Apple\''))]) == 1
assert len(
[f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('name ILIKE \'aPple\''))]) == 1
assert len(
[f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('name ILIKE \'%pp%\''))]) == 1
assert len([f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('cnt > 0'))]) == 4
assert len([f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('cnt < 0'))]) == 1
assert len([f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('cnt >= 100'))]) == 4
assert len([f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('cnt <= 100'))]) == 2
assert len(
[f for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression('pk IN (1, 2, 4, 8)'))]) == 3

def testGetFeaturesUncompiled(self):
try:
Expand All @@ -45,7 +49,7 @@ def testGetFeaturesCompiled(self):

def testGetFeaturesFilterRectTests(self):
extent = QgsRectangle(-70, 67, -60, 80)
features = [ f['pk'] for f in self.provider.getFeatures( QgsFeatureRequest().setFilterRect( extent ) ) ]
features = [f['pk'] for f in self.provider.getFeatures(QgsFeatureRequest().setFilterRect(extent))]
assert set(features) == set([2L, 4L]), 'Got {} instead'.format(features)

def testMinValue(self):
Expand All @@ -57,14 +61,16 @@ def testMaxValue(self):
assert self.provider.maximumValue(2) == 'Pear'

def testExtent(self):
reference = QgsGeometry.fromRect(QgsRectangle(-71.1230000000000047,66.3299999999999983,-65.3199999999999932,78.2999999999999972))
provider_extent=QgsGeometry.fromRect(self.provider.extent())
reference = QgsGeometry.fromRect(
QgsRectangle(-71.1230000000000047, 66.3299999999999983, -65.3199999999999932, 78.2999999999999972))
provider_extent = QgsGeometry.fromRect(self.provider.extent())

assert QgsGeometry.compare( provider_extent.asPolygon(), reference.asPolygon(), 0.000001)
assert QgsGeometry.compare(provider_extent.asPolygon(), reference.asPolygon(), 0.000001)

def testUnique(self):
assert set(self.provider.uniqueValues(1)) == set([-200, 100, 200, 300, 400])
assert set([u'Apple', u'Honey', u'Orange', u'Pear', NULL]) == set(self.provider.uniqueValues(2)), 'Got {}'.format(set(self.provider.uniqueValues(2)))
assert set([u'Apple', u'Honey', u'Orange', u'Pear', NULL]) == set(
self.provider.uniqueValues(2)), 'Got {}'.format(set(self.provider.uniqueValues(2)))

def testFeatureCount(self):
assert self.provider.featureCount() == 5
assert self.provider.featureCount() == 5
54 changes: 54 additions & 0 deletions tests/src/python/test_provider_shapefile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for the postgres provider.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Matthias Kuhn'
__date__ = '2015-04-23'
__copyright__ = 'Copyright 2015, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'

import qgis
import os

from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsProviderRegistry
from PyQt4.QtCore import QSettings
from utilities import (unitTestDataPath,
getQgisTestApp,
unittest,
TestCase
)
from providertestbase import ProviderTestCase

QGISAPP, CANVAS, IFACE, PARENT = getQgisTestApp()
TEST_DATA_DIR = unitTestDataPath()

class TestPyQgsPostgresProvider(TestCase, ProviderTestCase):
@classmethod
def setUpClass(cls):
"""Run before all tests"""
# Create test layer
cls.vl = QgsVectorLayer(u'{}/provider/shapefile.shp|layerid=0'.format(TEST_DATA_DIR), u'test', u'ogr' )
assert(cls.vl.isValid())
cls.provider = cls.vl.dataProvider()

@classmethod
def tearDownClass(cls):
"""Run after all tests"""

def testUnique(self):
"""
Override parent method because OGR doesn't return NULL values in SELECT DISTINCT...
This is only to make the tests pass, not to define this as expected behavior so if it is possible to remove this
in the future even better.
"""
assert set(self.provider.uniqueValues(1)) == set([-200, 100, 200, 300, 400])
assert set([u'Apple', u'Honey', u'Orange', u'Pear']) == set(self.provider.uniqueValues(2)), 'Got {}'.format(set(self.provider.uniqueValues(2)))


if __name__ == '__main__':
unittest.main()
Binary file added tests/testdata/provider/shapefile.dbf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/testdata/provider/shapefile.prj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
1 change: 1 addition & 0 deletions tests/testdata/provider/shapefile.qpj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
Binary file added tests/testdata/provider/shapefile.shp
Binary file not shown.
Binary file added tests/testdata/provider/shapefile.shx
Binary file not shown.

0 comments on commit afc3996

Please sign in to comment.