Skip to content

Commit

Permalink
Fixes #36054 : Fix loss precision when rect filtering in virtual layer
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 authored and nyalldawson committed May 28, 2020
1 parent 98603b7 commit b0424c3
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 13 deletions.
14 changes: 10 additions & 4 deletions src/providers/virtual/qgsvirtuallayerfeatureiterator.cpp
Expand Up @@ -70,18 +70,20 @@ QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerF
wheres << subset;
}

QVariantList binded;
if ( !mSource->mDefinition.uid().isNull() )
{
// filters are only available when a column with unique id exists
if ( mSource->mDefinition.hasDefinedGeometry() && !mFilterRect.isNull() )
{
bool do_exact = request.flags() & QgsFeatureRequest::ExactIntersect;
QString mbr = QStringLiteral( "%1,%2,%3,%4" ).arg( mFilterRect.xMinimum() ).arg( mFilterRect.yMinimum() ).arg( mFilterRect.xMaximum() ).arg( mFilterRect.yMaximum() );
wheres << quotedColumn( mSource->mDefinition.geometryField() ) + " is not null";
wheres << QStringLiteral( "%1Intersects(%2,BuildMbr(%3))" )
wheres << QStringLiteral( "%1Intersects(%2,BuildMbr(?,?,?,?))" )
.arg( do_exact ? "" : "Mbr",
quotedColumn( mSource->mDefinition.geometryField() ),
mbr );
quotedColumn( mSource->mDefinition.geometryField() ) );

binded << mFilterRect.xMinimum() << mFilterRect.yMinimum()
<< mFilterRect.xMaximum() << mFilterRect.yMaximum();
}
else if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
Expand Down Expand Up @@ -208,6 +210,10 @@ QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerF
}

mQuery.reset( new Sqlite::Query( mSource->mSqlite, mSqlQuery ) );
for ( QVariant toBind : binded )
{
mQuery->bind( toBind );
}

mFid = 0;
}
Expand Down
39 changes: 32 additions & 7 deletions src/providers/virtual/qgsvirtuallayersqlitehelper.cpp
Expand Up @@ -15,6 +15,7 @@ email : hugo dot mercier at oslandia dot com
***************************************************************************/

#include <QString>
#include <QVariant>

#include <stdexcept>

Expand Down Expand Up @@ -118,20 +119,44 @@ namespace Sqlite

int Query::step() { return sqlite3_step( stmt_ ); }

Query &Query::bind( const QString &str, int idx )
Query &Query::bind( const QVariant &value, int idx )
{
QByteArray ba( str.toUtf8() );
int r = sqlite3_bind_text( stmt_, idx, ba.constData(), ba.size(), SQLITE_TRANSIENT );
if ( r )
switch ( value.type() )
{
throw std::runtime_error( sqlite3_errmsg( db_ ) );
case QVariant::String:
{
QByteArray ba( value.toString().toUtf8() );
int r = sqlite3_bind_text( stmt_, idx, ba.constData(), ba.size(), SQLITE_TRANSIENT );
if ( r )
{
throw std::runtime_error( sqlite3_errmsg( db_ ) );
}
return *this;
}

case QVariant::Double:
{
bool ok; // no reason to fail double conversion
double dbl = value.toDouble( &ok );
int r = sqlite3_bind_double( stmt_, idx, dbl );
if ( r )
{
throw std::runtime_error( sqlite3_errmsg( db_ ) );
}
return *this;
}

default:
// Unsupported type
Q_ASSERT( false );
}

return *this;
}

Query &Query::bind( const QString &str )
Query &Query::bind( const QVariant &value )
{
return bind( str, nBind_++ );
return bind( value, nBind_++ );
}

void Query::exec( sqlite3 *db, const QString &sql )
Expand Down
4 changes: 2 additions & 2 deletions src/providers/virtual/qgsvirtuallayersqlitehelper.h
Expand Up @@ -60,8 +60,8 @@ namespace Sqlite

int step();

Query &bind( const QString &str, int idx );
Query &bind( const QString &str );
Query &bind( const QVariant &value, int idx );
Query &bind( const QVariant &value );

static void exec( sqlite3 *db, const QString &sql );

Expand Down
22 changes: 22 additions & 0 deletions tests/src/python/test_provider_virtual.py
Expand Up @@ -1142,6 +1142,28 @@ def testUpdatedFields(self):

QgsProject.instance().removeMapLayer(ml)

def test_filter_rect_precise(self):

# Check if don't lost precision when filtering on rect (see https://github.com/qgis/QGIS/issues/36054)

pl = QgsVectorLayer(os.path.join(self.testDataDir, "points.shp"), "points", "ogr")
self.assertTrue(pl.isValid())
QgsProject.instance().addMapLayer(pl)

query = toPercent("SELECT * FROM points")
vl = QgsVectorLayer("?query=%s&uid=OBJECTID" % (query), "vl", "virtual")
self.assertEqual(vl.isValid(), True)
self.assertEqual(vl.dataProvider().featureCount(), 17)

# Take an extent where east farthest point is excluded and second farthest east is on the edge
# and should be returned
extent = QgsRectangle(-117.23257418909581418, 22.80020703933767834, -85.6521739130433276, 46.87198067632875365)
r = QgsFeatureRequest(extent)
features = [feature for feature in vl.getFeatures(r)]
self.assertEqual(len(features), 16)

QgsProject.instance().removeMapLayer(pl)


if __name__ == '__main__':
unittest.main()

0 comments on commit b0424c3

Please sign in to comment.