Skip to content

Commit

Permalink
[vector layer] Fix minimumValue/maximumValue for joined and virtual f…
Browse files Browse the repository at this point in the history
…ields
  • Loading branch information
github-actions[bot] authored and nyalldawson committed May 15, 2020
1 parent 6f0c0e2 commit f9b067e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 17 deletions.
23 changes: 17 additions & 6 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -3991,21 +3991,32 @@ QVariant QgsVectorLayer::minimumOrMaximumValue( int index, bool minimum ) const
.setSubsetOfAttributes( attList ) ); .setSubsetOfAttributes( attList ) );


QgsFeature f; QgsFeature f;
double value = minimum ? std::numeric_limits<double>::max() : -std::numeric_limits<double>::max(); QVariant value;
double currentValue = 0; QVariant currentValue;
bool firstValue = true;
while ( fit.nextFeature( f ) ) while ( fit.nextFeature( f ) )
{ {
currentValue = f.attribute( index ).toDouble(); currentValue = f.attribute( index );
if ( ( minimum && currentValue < value ) || ( !minimum && currentValue > value ) ) if ( currentValue.isNull() )
continue;
if ( firstValue )
{ {
value = currentValue; value = currentValue;
firstValue = false;
}
else
{
if ( ( minimum && qgsVariantLessThan( currentValue, value ) ) || ( !minimum && qgsVariantGreaterThan( currentValue, value ) ) )
{
value = currentValue;
}
} }
} }
return QVariant( value ); return value;
} }
} }


Q_ASSERT_X( false, "QgsVectorLayer::minOrMax()", "Unknown source of the field!" ); Q_ASSERT_X( false, "QgsVectorLayer::minimumOrMaximum()", "Unknown source of the field!" );
return QVariant(); return QVariant();
} }


Expand Down
55 changes: 44 additions & 11 deletions tests/src/python/test_qgsvectorlayer.py
Expand Up @@ -16,7 +16,7 @@
import tempfile import tempfile
import shutil import shutil


from qgis.PyQt.QtCore import QVariant, Qt from qgis.PyQt.QtCore import QDate, QDateTime, QVariant, Qt
from qgis.PyQt.QtGui import QPainter, QColor from qgis.PyQt.QtGui import QPainter, QColor
from qgis.PyQt.QtXml import QDomDocument from qgis.PyQt.QtXml import QDomDocument


Expand Down Expand Up @@ -137,20 +137,20 @@ def createLayerWithFivePoints():


def createJoinLayer(): def createJoinLayer():
joinLayer = QgsVectorLayer( joinLayer = QgsVectorLayer(
"Point?field=x:string&field=y:integer&field=z:integer", "Point?field=x:string&field=y:integer&field=z:integer&field=date:datetime",
"joinlayer", "memory") "joinlayer", "memory")
pr = joinLayer.dataProvider() pr = joinLayer.dataProvider()
f1 = QgsFeature() f1 = QgsFeature()
f1.setAttributes(["foo", 123, 321]) f1.setAttributes(["foo", 123, 321, QDateTime(QDate(2010, 1, 1))])
f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 1))) f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 1)))
f2 = QgsFeature() f2 = QgsFeature()
f2.setAttributes(["bar", 456, 654]) f2.setAttributes(["bar", 456, 654, QDateTime(QDate(2020, 1, 1))])
f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2))) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2)))
f3 = QgsFeature() f3 = QgsFeature()
f3.setAttributes(["qar", 457, 111]) f3.setAttributes(["qar", 457, 111, None])
f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2))) f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2)))
f4 = QgsFeature() f4 = QgsFeature()
f4.setAttributes(["a", 458, 19]) f4.setAttributes(["a", 458, 19, QDateTime(QDate(2012, 1, 1))])
f4.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2))) f4.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2)))
assert pr.addFeatures([f1, f2, f3, f4]) assert pr.addFeatures([f1, f2, f3, f4])
assert joinLayer.featureCount() == 4 assert joinLayer.featureCount() == 4
Expand Down Expand Up @@ -1611,11 +1611,11 @@ def test_join(self):
layer.addJoin(join2) layer.addJoin(join2)


flds = layer.fields() flds = layer.fields()
self.assertEqual(len(flds), 6) self.assertEqual(len(flds), 8)
self.assertEqual(flds[2].name(), "joinlayer_x") self.assertEqual(flds[2].name(), "joinlayer_x")
self.assertEqual(flds[3].name(), "joinlayer_z") self.assertEqual(flds[3].name(), "joinlayer_z")
self.assertEqual(flds[4].name(), "custom-prefix_x") self.assertEqual(flds[5].name(), "custom-prefix_x")
self.assertEqual(flds[5].name(), "custom-prefix_z") self.assertEqual(flds[6].name(), "custom-prefix_z")
self.assertEqual(flds.fieldOrigin(0), QgsFields.OriginProvider) self.assertEqual(flds.fieldOrigin(0), QgsFields.OriginProvider)
self.assertEqual(flds.fieldOrigin(2), QgsFields.OriginJoin) self.assertEqual(flds.fieldOrigin(2), QgsFields.OriginJoin)
self.assertEqual(flds.fieldOrigin(3), QgsFields.OriginJoin) self.assertEqual(flds.fieldOrigin(3), QgsFields.OriginJoin)
Expand All @@ -1627,15 +1627,15 @@ def test_join(self):
fi = layer.getFeatures() fi = layer.getFeatures()
self.assertTrue(fi.nextFeature(f)) self.assertTrue(fi.nextFeature(f))
attrs = f.attributes() attrs = f.attributes()
self.assertEqual(len(attrs), 6) self.assertEqual(len(attrs), 8)
self.assertEqual(attrs[0], "test") self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123) self.assertEqual(attrs[1], 123)
self.assertEqual(attrs[2], "foo") self.assertEqual(attrs[2], "foo")
self.assertEqual(attrs[3], 321) self.assertEqual(attrs[3], 321)
self.assertFalse(fi.nextFeature(f)) self.assertFalse(fi.nextFeature(f))


f2 = next(layer.getFeatures(QgsFeatureRequest(f.id()))) f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(len(f2.attributes()), 6) self.assertEqual(len(f2.attributes()), 8)
self.assertEqual(f2[2], "foo") self.assertEqual(f2[2], "foo")
self.assertEqual(f2[3], 321) self.assertEqual(f2[3], 321)


Expand All @@ -1653,8 +1653,19 @@ def test_JoinStats(self):
layer.addJoin(join) layer.addJoin(join)


# stats on joined fields should only include values present by join # stats on joined fields should only include values present by join

# strings
self.assertEqual(layer.minimumValue(2), "foo")
self.assertEqual(layer.maximumValue(2), "qar")

# numbers
self.assertEqual(layer.minimumValue(3), 111) self.assertEqual(layer.minimumValue(3), 111)
self.assertEqual(layer.maximumValue(3), 321) self.assertEqual(layer.maximumValue(3), 321)

# dates (maximumValue also tests we properly handle null values by skipping those)
self.assertEqual(layer.minimumValue(4), QDateTime(QDate(2010, 1, 1)))
self.assertEqual(layer.maximumValue(4), QDateTime(QDate(2010, 1, 1)))

self.assertEqual(set(layer.uniqueValues(3)), set([111, 321])) self.assertEqual(set(layer.uniqueValues(3)), set([111, 321]))


def test_valid_join_when_opening_project(self): def test_valid_join_when_opening_project(self):
Expand Down Expand Up @@ -1872,6 +1883,28 @@ def testMaxValue(self):
self.assertTrue(layer.changeAttributeValue(f1_id, 1, 1001)) self.assertTrue(layer.changeAttributeValue(f1_id, 1, 1001))
self.assertEqual(layer.maximumValue(1), 1001) self.assertEqual(layer.maximumValue(1), 1001)


def testMinMaxInVirtualField(self):
"""
Test minimum and maximum values in a virtual field
"""
layer = QgsVectorLayer("Point?field=fldstr:string", "layer", "memory")
pr = layer.dataProvider()

int_values = ['2010-01-01', None, '2020-01-01']
features = []
for i in int_values:
f = QgsFeature()
f.setFields(layer.fields())
f.setAttributes([i])
features.append(f)
assert pr.addFeatures(features)

field = QgsField('virtual', QVariant.Date)
layer.addExpressionField('to_date("fldstr")', field)
self.assertEqual(len(layer.getFeature(1).attributes()), 2)
self.assertEqual(layer.minimumValue(1), QDate(2010, 1, 1))
self.assertEqual(layer.maximumValue(1), QDate(2020, 1, 1))

def test_InvalidOperations(self): def test_InvalidOperations(self):
layer = createLayerWithOnePoint() layer = createLayerWithOnePoint()


Expand Down
Binary file modified tests/testdata/polys_overlapping_with_id.dbf
Binary file not shown.

0 comments on commit f9b067e

Please sign in to comment.