QGIS/tests/src/python/test_qgsvectorlayer.py
2017-10-26 07:06:34 +10:00

2911 lines
111 KiB
Python

# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsVectorLayer.
.. 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__ = 'Tim Sutton'
__date__ = '20/08/2012'
__copyright__ = 'Copyright 2012, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import qgis # NOQA
import os
from qgis.PyQt.QtCore import QVariant, Qt
from qgis.PyQt.QtGui import QPainter
from qgis.PyQt.QtXml import QDomDocument
from qgis.core import (QgsWkbTypes,
QgsAction,
QgsDefaultValue,
QgsEditorWidgetSetup,
QgsVectorLayer,
QgsRectangle,
QgsFeature,
QgsFeatureRequest,
QgsGeometry,
QgsPointXY,
QgsField,
QgsFieldConstraints,
QgsFields,
QgsVectorLayerJoinInfo,
QgsSymbol,
QgsSingleSymbolRenderer,
QgsCoordinateReferenceSystem,
QgsVectorLayerCache,
QgsReadWriteContext,
QgsProject,
QgsUnitTypes,
QgsAggregateCalculator,
QgsPoint,
QgsExpressionContext,
QgsExpressionContextScope,
QgsExpressionContextUtils,
QgsLineSymbol,
QgsMapLayerStyle,
QgsMapLayerDependency,
QgsPalLayerSettings,
QgsVectorLayerSimpleLabeling,
QgsSingleCategoryDiagramRenderer,
QgsDiagramLayerSettings,
QgsTextFormat,
QgsVectorLayerSelectedFeatureSource,
QgsExpression,
NULL)
from qgis.gui import (QgsAttributeTableModel,
QgsGui
)
from qgis.testing import start_app, unittest
from featuresourcetestbase import FeatureSourceTestCase
from utilities import unitTestDataPath
start_app()
def createEmptyLayer():
layer = QgsVectorLayer("Point", "addfeat", "memory")
assert layer.pendingFeatureCount() == 0
return layer
def createEmptyLayerWithFields():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory")
assert layer.pendingFeatureCount() == 0
return layer
def createLayerWithOnePoint():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f = QgsFeature()
f.setAttributes(["test", 123])
f.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
assert pr.addFeatures([f])
assert layer.pendingFeatureCount() == 1
return layer
def createLayerWithTwoPoints():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f = QgsFeature()
f.setAttributes(["test", 123])
f.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
f2 = QgsFeature()
f2.setAttributes(["test2", 457])
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
assert pr.addFeatures([f, f2])
assert layer.pendingFeatureCount() == 2
return layer
def createLayerWithFivePoints():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f = QgsFeature()
f.setAttributes(["test", 123])
f.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
f2 = QgsFeature()
f2.setAttributes(["test2", 457])
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(200, 200)))
f3 = QgsFeature()
f3.setAttributes(["test2", 888])
f3.setGeometry(QgsGeometry.fromPoint(QgsPointXY(300, 200)))
f4 = QgsFeature()
f4.setAttributes(["test3", -1])
f4.setGeometry(QgsGeometry.fromPoint(QgsPointXY(400, 300)))
f5 = QgsFeature()
f5.setAttributes(["test4", 0])
f5.setGeometry(QgsGeometry.fromPoint(QgsPointXY(0, 0)))
assert pr.addFeatures([f, f2, f3, f4, f5])
assert layer.featureCount() == 5
return layer
def createJoinLayer():
joinLayer = QgsVectorLayer(
"Point?field=x:string&field=y:integer&field=z:integer",
"joinlayer", "memory")
pr = joinLayer.dataProvider()
f1 = QgsFeature()
f1.setAttributes(["foo", 123, 321])
f1.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 1)))
f2 = QgsFeature()
f2.setAttributes(["bar", 456, 654])
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(2, 2)))
f3 = QgsFeature()
f3.setAttributes(["qar", 457, 111])
f3.setGeometry(QgsGeometry.fromPoint(QgsPointXY(2, 2)))
f4 = QgsFeature()
f4.setAttributes(["a", 458, 19])
f4.setGeometry(QgsGeometry.fromPoint(QgsPointXY(2, 2)))
assert pr.addFeatures([f1, f2, f3, f4])
assert joinLayer.pendingFeatureCount() == 4
return joinLayer
def dumpFeature(f):
print("--- FEATURE DUMP ---")
print(("valid: %d | id: %d" % (f.isValid(), f.id())))
geom = f.geometry()
if geom:
print(("geometry wkb: %d" % geom.wkbType()))
else:
print("no geometry")
print(("attrs: %s" % str(f.attributes())))
def formatAttributes(attrs):
return repr([str(a) for a in attrs])
def dumpEditBuffer(layer):
editBuffer = layer.editBuffer()
if not editBuffer:
print("NO EDITING!")
return
print("ADDED:")
for fid, f in editBuffer.addedFeatures().items():
print(("%d: %s | %s" % (
f.id(), formatAttributes(f.attributes()),
f.geometry().exportToWkt())))
print("CHANGED GEOM:")
for fid, geom in editBuffer.changedGeometries().items():
print(("%d | %s" % (f.id(), f.geometry().exportToWkt())))
class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase):
@classmethod
def getSource(cls):
vl = QgsVectorLayer(
'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk',
'test', 'memory')
assert (vl.isValid())
f1 = QgsFeature()
f1.setAttributes([5, -200, NULL, 'NuLl', '5'])
f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
f2 = QgsFeature()
f2.setAttributes([3, 300, 'Pear', 'PEaR', '3'])
f3 = QgsFeature()
f3.setAttributes([1, 100, 'Orange', 'oranGe', '1'])
f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)'))
f4 = QgsFeature()
f4.setAttributes([2, 200, 'Apple', 'Apple', '2'])
f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)'))
f5 = QgsFeature()
f5.setAttributes([4, 400, 'Honey', 'Honey', '4'])
f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)'))
vl.dataProvider().addFeatures([f1, f2, f3, f4, f5])
return vl
@classmethod
def setUpClass(cls):
"""Run before all tests"""
QgsGui.editorWidgetRegistry().initEditors()
# Create test layer for FeatureSourceTestCase
cls.source = cls.getSource()
def testGetFeaturesSubsetAttributes2(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testGetFeaturesNoGeometry(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def test_FeatureCount(self):
myPath = os.path.join(unitTestDataPath(), 'lines.shp')
myLayer = QgsVectorLayer(myPath, 'Lines', 'ogr')
myCount = myLayer.featureCount()
self.assertEqual(myCount, 6)
# ADD FEATURE
def test_AddFeature(self):
layer = createEmptyLayerWithFields()
feat = QgsFeature(layer.fields())
feat.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 2)))
def checkAfter():
self.assertEqual(layer.pendingFeatureCount(), 1)
# check select+nextFeature
f = next(layer.getFeatures())
self.assertEqual(f.geometry().asPoint(), QgsPointXY(1, 2))
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2.geometry().asPoint(), QgsPointXY(1, 2))
def checkBefore():
self.assertEqual(layer.pendingFeatureCount(), 0)
# check select+nextFeature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
checkBefore()
# try to add feature without editing mode
self.assertFalse(layer.addFeature(feat))
# add feature
layer.startEditing()
# try adding feature with incorrect number of fields
bad_feature = QgsFeature()
self.assertFalse(layer.addFeature(bad_feature))
# add good feature
self.assertTrue(layer.addFeature(feat))
checkAfter()
self.assertEqual(layer.dataProvider().featureCount(), 0)
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
self.assertEqual(layer.dataProvider().featureCount(), 1)
# ADD FEATURES
def test_AddFeatures(self):
layer = createEmptyLayerWithFields()
feat1 = QgsFeature(layer.fields())
feat1.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 2)))
feat2 = QgsFeature(layer.fields())
feat2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(11, 12)))
def checkAfter():
self.assertEqual(layer.pendingFeatureCount(), 2)
# check select+nextFeature
it = layer.getFeatures()
f1 = next(it)
self.assertEqual(f1.geometry().asPoint(), QgsPointXY(1, 2))
f2 = next(it)
self.assertEqual(f2.geometry().asPoint(), QgsPointXY(11, 12))
# check feature at id
f1_1 = next(layer.getFeatures(QgsFeatureRequest(f1.id())))
self.assertEqual(f1_1.geometry().asPoint(), QgsPointXY(1, 2))
f2_1 = next(layer.getFeatures(QgsFeatureRequest(f2.id())))
self.assertEqual(f2_1.geometry().asPoint(), QgsPointXY(11, 12))
def checkBefore():
self.assertEqual(layer.pendingFeatureCount(), 0)
# check select+nextFeature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
checkBefore()
# try to add feature without editing mode
self.assertFalse(layer.addFeatures([feat1, feat2]))
# add feature
layer.startEditing()
# try adding feature with incorrect number of fields
bad_feature = QgsFeature()
self.assertFalse(layer.addFeatures([bad_feature]))
# add good features
self.assertTrue(layer.addFeatures([feat1, feat2]))
checkAfter()
self.assertEqual(layer.dataProvider().featureCount(), 0)
# now try undo/redo
layer.undoStack().undo()
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
self.assertEqual(layer.dataProvider().featureCount(), 2)
# DELETE FEATURE
def test_DeleteFeature(self):
layer = createLayerWithOnePoint()
fid = 1
def checkAfter():
self.assertEqual(layer.pendingFeatureCount(), 0)
# check select+nextFeature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
# check feature at id
with self.assertRaises(StopIteration):
next(layer.getFeatures(QgsFeatureRequest(fid)))
def checkBefore():
self.assertEqual(layer.pendingFeatureCount(), 1)
# check select+nextFeature
fi = layer.getFeatures()
f = next(fi)
self.assertEqual(f.geometry().asPoint(), QgsPointXY(100, 200))
with self.assertRaises(StopIteration):
next(fi)
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(fid)))
self.assertEqual(f2.id(), fid)
checkBefore()
# try to delete feature without editing mode
self.assertFalse(layer.deleteFeature(fid))
# delete feature
layer.startEditing()
self.assertTrue(layer.deleteFeature(fid))
checkAfter()
# make sure calling it twice does not work
self.assertFalse(layer.deleteFeature(fid))
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertEqual(layer.dataProvider().featureCount(), 1)
self.assertTrue(layer.commitChanges())
checkAfter()
self.assertEqual(layer.dataProvider().featureCount(), 0)
def test_DeleteFeatureAfterAddFeature(self):
layer = createEmptyLayer()
feat = QgsFeature()
feat.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 2)))
def checkBefore():
self.assertEqual(layer.pendingFeatureCount(), 0)
# check select+nextFeature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
def checkAfter1():
self.assertEqual(layer.pendingFeatureCount(), 1)
def checkAfter2():
checkBefore() # should be the same state: no features
checkBefore()
# add feature
layer.startEditing()
self.assertTrue(layer.addFeature(feat))
checkAfter1()
fid = feat.id()
self.assertTrue(layer.deleteFeature(fid))
checkAfter2()
# now try undo/redo
layer.undoStack().undo()
checkAfter1()
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter1()
layer.undoStack().redo()
checkAfter2()
self.assertTrue(layer.commitChanges())
checkAfter2()
self.assertEqual(layer.dataProvider().featureCount(), 0)
def test_DeleteJoinedFeature(self):
joinLayer = createJoinLayer()
joinLayer2 = createJoinLayer()
QgsProject.instance().addMapLayers([joinLayer, joinLayer2])
layer = createLayerWithOnePoint()
join = QgsVectorLayerJoinInfo()
join.setTargetFieldName("fldint")
join.setJoinLayer(joinLayer)
join.setJoinFieldName("y")
join.setUsingMemoryCache(True)
join.setEditable(True)
join.setCascadedDelete(True)
layer.addJoin(join)
join2 = QgsVectorLayerJoinInfo()
join2.setTargetFieldName("fldint")
join2.setJoinLayer(joinLayer2)
join2.setJoinFieldName("y")
join2.setUsingMemoryCache(True)
join2.setPrefix("custom-prefix_")
join2.setEditable(True)
join2.setCascadedDelete(False)
layer.addJoin(join2)
# check number of features
self.assertEqual(layer.featureCount(), 1)
self.assertEqual(joinLayer.featureCount(), 4)
self.assertEqual(joinLayer2.featureCount(), 4)
# delete a feature which is also in joined layers
layer.startEditing()
joinLayer.startEditing()
joinLayer2.startEditing()
filter = QgsExpression.createFieldEqualityExpression('fldint', '123')
feature = next(layer.getFeatures(QgsFeatureRequest().setFilterExpression(filter)))
layer.deleteFeature(feature.id())
# check number of features
self.assertEqual(layer.featureCount(), 0)
self.assertEqual(joinLayer.featureCount(), 3) # deleteCascade activated
self.assertEqual(joinLayer2.featureCount(), 4) # deleteCascade deactivated
# CHANGE ATTRIBUTE
def test_ChangeAttribute(self):
layer = createLayerWithOnePoint()
fid = 1
def checkAfter():
# check select+nextFeature
fi = layer.getFeatures()
f = next(fi)
self.assertEqual(f[0], "good")
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2[0], "good")
def checkBefore():
# check select+nextFeature
f = next(layer.getFeatures())
self.assertEqual(f[0], "test")
checkBefore()
# try to change attribute without editing mode
self.assertFalse(layer.changeAttributeValue(fid, 0, "good"))
# change attribute
layer.startEditing()
self.assertTrue(layer.changeAttributeValue(fid, 0, "good"))
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
def test_ChangeAttributeAfterAddFeature(self):
layer = createLayerWithOnePoint()
layer.dataProvider().deleteFeatures([1]) # no need for this feature
newF = QgsFeature()
newF.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 1)))
newF.setAttributes(["hello", 42])
def checkAfter():
self.assertEqual(len(layer.pendingFields()), 2)
# check feature
fi = layer.getFeatures()
f = next(fi)
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], "hello")
self.assertEqual(attrs[1], 12)
with self.assertRaises(StopIteration):
next(fi)
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2[0], "hello")
self.assertEqual(f2[1], 12)
def checkBefore():
# check feature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
checkBefore()
layer.startEditing()
layer.beginEditCommand("AddFeature + ChangeAttribute")
self.assertTrue(layer.addFeature(newF))
self.assertTrue(layer.changeAttributeValue(newF.id(), 1, 12))
layer.endEditCommand()
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
# print "COMMIT ERRORS:"
# for item in list(layer.commitErrors()): print item
# CHANGE GEOMETRY
def test_ChangeGeometry(self):
layer = createLayerWithOnePoint()
fid = 1
def checkAfter():
# check select+nextFeature
f = next(layer.getFeatures())
self.assertEqual(f.geometry().asPoint(), QgsPointXY(300, 400))
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2.geometry().asPoint(), QgsPointXY(300, 400))
def checkBefore():
# check select+nextFeature
f = next(layer.getFeatures())
self.assertEqual(f.geometry().asPoint(), QgsPointXY(100, 200))
# try to change geometry without editing mode
self.assertFalse(layer.changeGeometry(fid, QgsGeometry.fromPoint(QgsPointXY(300, 400))))
checkBefore()
# change geometry
layer.startEditing()
layer.beginEditCommand("ChangeGeometry")
self.assertTrue(layer.changeGeometry(fid, QgsGeometry.fromPoint(QgsPointXY(300, 400))))
layer.endEditCommand()
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
def test_ChangeGeometryAfterChangeAttribute(self):
layer = createLayerWithOnePoint()
fid = 1
def checkAfter():
# check select+nextFeature
f = next(layer.getFeatures())
self.assertEqual(f.geometry().asPoint(), QgsPointXY(300, 400))
self.assertEqual(f[0], "changed")
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2.geometry().asPoint(), QgsPointXY(300, 400))
self.assertEqual(f2[0], "changed")
def checkBefore():
# check select+nextFeature
f = next(layer.getFeatures())
self.assertEqual(f.geometry().asPoint(), QgsPointXY(100, 200))
self.assertEqual(f[0], "test")
checkBefore()
# change geometry
layer.startEditing()
layer.beginEditCommand("ChangeGeometry + ChangeAttribute")
self.assertTrue(layer.changeAttributeValue(fid, 0, "changed"))
self.assertTrue(layer.changeGeometry(fid, QgsGeometry.fromPoint(QgsPointXY(300, 400))))
layer.endEditCommand()
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
def test_ChangeGeometryAfterAddFeature(self):
layer = createLayerWithOnePoint()
layer.dataProvider().deleteFeatures([1]) # no need for this feature
newF = QgsFeature()
newF.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 1)))
newF.setAttributes(["hello", 42])
def checkAfter():
self.assertEqual(len(layer.pendingFields()), 2)
# check feature
f = next(layer.getFeatures())
self.assertEqual(f.geometry().asPoint(), QgsPointXY(2, 2))
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2.geometry().asPoint(), QgsPointXY(2, 2))
def checkBefore():
# check feature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
checkBefore()
layer.startEditing()
layer.beginEditCommand("AddFeature+ChangeGeometry")
self.assertTrue(layer.addFeature(newF))
self.assertTrue(layer.changeGeometry(newF.id(), QgsGeometry.fromPoint(QgsPointXY(2, 2))))
layer.endEditCommand()
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
self.assertTrue(layer.commitChanges())
checkAfter()
# print "COMMIT ERRORS:"
# for item in list(layer.commitErrors()): print item
# ADD ATTRIBUTE
def test_AddAttribute(self):
layer = createLayerWithOnePoint()
fld1 = QgsField("fld1", QVariant.Int, "integer")
#fld2 = QgsField("fld2", QVariant.Int, "integer")
def checkBefore():
# check fields
flds = layer.pendingFields()
self.assertEqual(len(flds), 2)
self.assertEqual(flds[0].name(), "fldtxt")
self.assertEqual(flds[1].name(), "fldint")
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123)
def checkAfter():
# check fields
flds = layer.pendingFields()
self.assertEqual(len(flds), 3)
self.assertEqual(flds[0].name(), "fldtxt")
self.assertEqual(flds[1].name(), "fldint")
self.assertEqual(flds[2].name(), "fld1")
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 3)
self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123)
self.assertTrue(attrs[2] is None)
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2[0], "test")
self.assertEqual(f2[1], 123)
self.assertTrue(f2[2] is None)
# for nt in layer.dataProvider().nativeTypes():
# print (nt.mTypeDesc, nt.mTypeName, nt.mType, nt.mMinLen,
# nt.mMaxLen, nt.mMinPrec, nt.mMaxPrec)
self.assertTrue(layer.dataProvider().supportedType(fld1))
# without editing mode
self.assertFalse(layer.addAttribute(fld1))
layer.startEditing()
checkBefore()
self.assertTrue(layer.addAttribute(fld1))
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
layer.commitChanges()
checkAfter()
def test_AddAttributeAfterAddFeature(self):
layer = createLayerWithOnePoint()
layer.dataProvider().deleteFeatures([1]) # no need for this feature
newF = QgsFeature()
newF.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 1)))
newF.setAttributes(["hello", 42])
fld1 = QgsField("fld1", QVariant.Int, "integer")
def checkBefore():
self.assertEqual(len(layer.pendingFields()), 2)
# check feature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
def checkAfter():
self.assertEqual(len(layer.pendingFields()), 3)
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 3)
self.assertEqual(attrs[0], "hello")
self.assertEqual(attrs[1], 42)
self.assertTrue(attrs[2] is None)
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(f2[0], "hello")
self.assertEqual(f2[1], 42)
self.assertTrue(f2[2] is None)
layer.startEditing()
checkBefore()
layer.beginEditCommand("AddFeature + AddAttribute")
self.assertTrue(layer.addFeature(newF))
self.assertTrue(layer.addAttribute(fld1))
layer.endEditCommand()
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter()
layer.commitChanges()
checkAfter()
# print "COMMIT ERRORS:"
# for item in list(layer.commitErrors()): print item
def test_AddAttributeAfterChangeValue(self):
pass # not interesting to test...?
def test_AddAttributeAfterDeleteAttribute(self):
pass # maybe it would be good to test
# DELETE ATTRIBUTE
def test_DeleteAttribute(self):
layer = createLayerWithOnePoint()
layer.dataProvider().addAttributes(
[QgsField("flddouble", QVariant.Double, "double")])
layer.dataProvider().changeAttributeValues(
{1: {2: 5.5}})
# without editing mode
self.assertFalse(layer.deleteAttribute(0))
def checkBefore():
flds = layer.pendingFields()
self.assertEqual(len(flds), 3)
self.assertEqual(flds[0].name(), "fldtxt")
self.assertEqual(flds[1].name(), "fldint")
self.assertEqual(flds[2].name(), "flddouble")
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 3)
self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123)
self.assertEqual(attrs[2], 5.5)
layer.startEditing()
checkBefore()
self.assertTrue(layer.deleteAttribute(0))
def checkAfterOneDelete():
flds = layer.pendingFields()
# for fld in flds: print "FLD", fld.name()
self.assertEqual(len(flds), 2)
self.assertEqual(flds[0].name(), "fldint")
self.assertEqual(flds[1].name(), "flddouble")
self.assertEqual(layer.pendingAllAttributesList(), [0, 1])
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], 123)
self.assertEqual(attrs[1], 5.5)
checkAfterOneDelete()
# delete last attribute
self.assertTrue(layer.deleteAttribute(0))
def checkAfterTwoDeletes():
self.assertEqual(layer.pendingAllAttributesList(), [0])
flds = layer.pendingFields()
# for fld in flds: print "FLD", fld.name()
self.assertEqual(len(flds), 1)
self.assertEqual(flds[0].name(), "flddouble")
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 1)
self.assertEqual(attrs[0], 5.5)
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(len(f2.attributes()), 1)
self.assertEqual(f2[0], 5.5)
checkAfterTwoDeletes()
layer.undoStack().undo()
checkAfterOneDelete()
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfterOneDelete()
layer.undoStack().redo()
checkAfterTwoDeletes()
self.assertTrue(layer.commitChanges()) # COMMIT!
checkAfterTwoDeletes()
def test_DeleteAttributeAfterAddAttribute(self):
layer = createLayerWithOnePoint()
fld1 = QgsField("fld1", QVariant.Int, "integer")
def checkAfter(): # layer should be unchanged
flds = layer.pendingFields()
self.assertEqual(len(flds), 2)
self.assertEqual(flds[0].name(), "fldtxt")
self.assertEqual(flds[1].name(), "fldint")
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123)
# check feature at id
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(len(f2.attributes()), 2)
self.assertEqual(f2[0], "test")
self.assertEqual(f2[1], 123)
checkAfter()
layer.startEditing()
layer.beginEditCommand("AddAttribute + DeleteAttribute")
self.assertTrue(layer.addAttribute(fld1))
self.assertTrue(layer.deleteAttribute(2))
layer.endEditCommand()
checkAfter()
# now try undo/redo
layer.undoStack().undo()
checkAfter()
layer.undoStack().redo()
checkAfter()
layer.commitChanges()
checkAfter()
def test_DeleteAttributeAfterAddFeature(self):
layer = createLayerWithOnePoint()
layer.dataProvider().deleteFeatures([1]) # no need for this feature
newF = QgsFeature()
newF.setGeometry(QgsGeometry.fromPoint(QgsPointXY(1, 1)))
newF.setAttributes(["hello", 42])
def checkBefore():
self.assertEqual(len(layer.pendingFields()), 2)
# check feature
with self.assertRaises(StopIteration):
next(layer.getFeatures())
def checkAfter1():
self.assertEqual(len(layer.pendingFields()), 2)
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], "hello")
self.assertEqual(attrs[1], 42)
def checkAfter2():
self.assertEqual(len(layer.pendingFields()), 1)
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 1)
self.assertEqual(attrs[0], 42)
layer.startEditing()
checkBefore()
layer.addFeature(newF)
checkAfter1()
layer.deleteAttribute(0)
checkAfter2()
# now try undo/redo
layer.undoStack().undo()
checkAfter1()
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter1()
layer.undoStack().redo()
checkAfter2()
layer.commitChanges()
checkAfter2()
def test_DeleteAttributeAfterChangeValue(self):
layer = createLayerWithOnePoint()
def checkBefore():
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123)
def checkAfter1():
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 2)
self.assertEqual(attrs[0], "changed")
self.assertEqual(attrs[1], 123)
def checkAfter2():
# check feature
f = next(layer.getFeatures())
attrs = f.attributes()
self.assertEqual(len(attrs), 1)
self.assertEqual(attrs[0], 123)
layer.startEditing()
checkBefore()
self.assertTrue(layer.changeAttributeValue(1, 0, "changed"))
checkAfter1()
self.assertTrue(layer.deleteAttribute(0))
checkAfter2()
# now try undo/redo
layer.undoStack().undo()
checkAfter1()
layer.undoStack().undo()
checkBefore()
layer.undoStack().redo()
checkAfter1()
layer.undoStack().redo()
checkAfter2()
layer.commitChanges()
checkAfter2()
# RENAME ATTRIBUTE
def test_RenameAttribute(self):
layer = createLayerWithOnePoint()
# without editing mode
self.assertFalse(layer.renameAttribute(0, 'renamed'))
def checkFieldNames(names):
flds = layer.fields()
f = next(layer.getFeatures())
self.assertEqual(flds.count(), len(names))
self.assertEqual(f.fields().count(), len(names))
for idx, expected_name in enumerate(names):
self.assertEqual(flds[idx].name(), expected_name)
self.assertEqual(f.fields().at(idx).name(), expected_name)
layer.startEditing()
checkFieldNames(['fldtxt', 'fldint'])
self.assertFalse(layer.renameAttribute(-1, 'fldtxt2'))
self.assertFalse(layer.renameAttribute(10, 'fldtxt2'))
self.assertFalse(layer.renameAttribute(0, 'fldint')) # duplicate name
self.assertTrue(layer.renameAttribute(0, 'fldtxt2'))
checkFieldNames(['fldtxt2', 'fldint'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint'])
layer.undoStack().redo()
checkFieldNames(['fldtxt2', 'fldint'])
# change two fields
self.assertTrue(layer.renameAttribute(1, 'fldint2'))
checkFieldNames(['fldtxt2', 'fldint2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt2', 'fldint'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint'])
layer.undoStack().redo()
checkFieldNames(['fldtxt2', 'fldint'])
layer.undoStack().redo()
checkFieldNames(['fldtxt2', 'fldint2'])
# two renames
self.assertTrue(layer.renameAttribute(0, 'fldtxt3'))
checkFieldNames(['fldtxt3', 'fldint2'])
self.assertTrue(layer.renameAttribute(0, 'fldtxt4'))
checkFieldNames(['fldtxt4', 'fldint2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt3', 'fldint2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt2', 'fldint2'])
layer.undoStack().redo()
checkFieldNames(['fldtxt3', 'fldint2'])
layer.undoStack().redo()
checkFieldNames(['fldtxt4', 'fldint2'])
def test_RenameAttributeAfterAdd(self):
layer = createLayerWithOnePoint()
def checkFieldNames(names):
flds = layer.fields()
f = next(layer.getFeatures())
self.assertEqual(flds.count(), len(names))
self.assertEqual(f.fields().count(), len(names))
for idx, expected_name in enumerate(names):
self.assertEqual(flds[idx].name(), expected_name)
self.assertEqual(f.fields().at(idx).name(), expected_name)
layer.startEditing()
checkFieldNames(['fldtxt', 'fldint'])
self.assertTrue(layer.renameAttribute(1, 'fldint2'))
checkFieldNames(['fldtxt', 'fldint2'])
#add an attribute
self.assertTrue(layer.addAttribute(QgsField("flddouble", QVariant.Double, "double")))
checkFieldNames(['fldtxt', 'fldint2', 'flddouble'])
# rename it
self.assertTrue(layer.renameAttribute(2, 'flddouble2'))
checkFieldNames(['fldtxt', 'fldint2', 'flddouble2'])
self.assertTrue(layer.addAttribute(QgsField("flddate", QVariant.Date, "date")))
checkFieldNames(['fldtxt', 'fldint2', 'flddouble2', 'flddate'])
self.assertTrue(layer.renameAttribute(2, 'flddouble3'))
checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate'])
self.assertTrue(layer.renameAttribute(3, 'flddate2'))
checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble2', 'flddate'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint'])
layer.undoStack().redo()
checkFieldNames(['fldtxt', 'fldint2'])
layer.undoStack().redo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble'])
layer.undoStack().redo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble2'])
layer.undoStack().redo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble2', 'flddate'])
layer.undoStack().redo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate'])
layer.undoStack().redo()
checkFieldNames(['fldtxt', 'fldint2', 'flddouble3', 'flddate2'])
def test_RenameAttributeAndDelete(self):
layer = createLayerWithOnePoint()
layer.dataProvider().addAttributes(
[QgsField("flddouble", QVariant.Double, "double")])
layer.updateFields()
def checkFieldNames(names):
flds = layer.fields()
f = next(layer.getFeatures())
self.assertEqual(flds.count(), len(names))
self.assertEqual(f.fields().count(), len(names))
for idx, expected_name in enumerate(names):
self.assertEqual(flds[idx].name(), expected_name)
self.assertEqual(f.fields().at(idx).name(), expected_name)
layer.startEditing()
checkFieldNames(['fldtxt', 'fldint', 'flddouble'])
self.assertTrue(layer.renameAttribute(0, 'fldtxt2'))
checkFieldNames(['fldtxt2', 'fldint', 'flddouble'])
self.assertTrue(layer.renameAttribute(2, 'flddouble2'))
checkFieldNames(['fldtxt2', 'fldint', 'flddouble2'])
#delete an attribute
self.assertTrue(layer.deleteAttribute(0))
checkFieldNames(['fldint', 'flddouble2'])
# rename remaining
self.assertTrue(layer.renameAttribute(0, 'fldint2'))
checkFieldNames(['fldint2', 'flddouble2'])
self.assertTrue(layer.renameAttribute(1, 'flddouble3'))
checkFieldNames(['fldint2', 'flddouble3'])
#delete an attribute
self.assertTrue(layer.deleteAttribute(0))
checkFieldNames(['flddouble3'])
self.assertTrue(layer.renameAttribute(0, 'flddouble4'))
checkFieldNames(['flddouble4'])
layer.undoStack().undo()
checkFieldNames(['flddouble3'])
layer.undoStack().undo()
checkFieldNames(['fldint2', 'flddouble3'])
layer.undoStack().undo()
checkFieldNames(['fldint2', 'flddouble2'])
layer.undoStack().undo()
checkFieldNames(['fldint', 'flddouble2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt2', 'fldint', 'flddouble2'])
layer.undoStack().undo()
checkFieldNames(['fldtxt2', 'fldint', 'flddouble'])
layer.undoStack().undo()
checkFieldNames(['fldtxt', 'fldint', 'flddouble'])
#layer.undoStack().redo()
#checkFieldNames(['fldtxt2', 'fldint'])
#layer.undoStack().redo()
#checkFieldNames(['fldint'])
def test_RenameExpressionField(self):
layer = createLayerWithOnePoint()
exp_field_idx = layer.addExpressionField('1+1', QgsField('math_is_hard', QVariant.Int))
#rename and check
self.assertTrue(layer.renameAttribute(exp_field_idx, 'renamed'))
self.assertEqual(layer.fields()[exp_field_idx].name(), 'renamed')
f = next(layer.getFeatures())
self.assertEqual(f.fields()[exp_field_idx].name(), 'renamed')
def test_fields(self):
layer = createLayerWithOnePoint()
flds = layer.pendingFields()
self.assertEqual(flds.indexFromName("fldint"), 1)
self.assertEqual(flds.indexFromName("fldXXX"), -1)
def test_getFeatures(self):
layer = createLayerWithOnePoint()
f = QgsFeature()
fi = layer.getFeatures()
self.assertTrue(fi.nextFeature(f))
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 1)
self.assertEqual(f.geometry().asPoint(), QgsPointXY(100, 200))
self.assertEqual(f["fldtxt"], "test")
self.assertEqual(f["fldint"], 123)
self.assertFalse(fi.nextFeature(f))
layer2 = createLayerWithFivePoints()
# getFeature(fid)
feat = layer2.getFeature(4)
self.assertTrue(feat.isValid())
self.assertEqual(feat['fldtxt'], 'test3')
self.assertEqual(feat['fldint'], -1)
feat = layer2.getFeature(10)
self.assertFalse(feat.isValid())
# getFeatures(expression)
it = layer2.getFeatures("fldint <= 0")
fids = [f.id() for f in it]
self.assertEqual(set(fids), set([4, 5]))
# getFeatures(fids)
it = layer2.getFeatures([1, 2])
fids = [f.id() for f in it]
self.assertEqual(set(fids), set([1, 2]))
# getFeatures(rect)
it = layer2.getFeatures(QgsRectangle(99, 99, 201, 201))
fids = [f.id() for f in it]
self.assertEqual(set(fids), set([1, 2]))
def test_join(self):
joinLayer = createJoinLayer()
joinLayer2 = createJoinLayer()
QgsProject.instance().addMapLayers([joinLayer, joinLayer2])
layer = createLayerWithOnePoint()
join = QgsVectorLayerJoinInfo()
join.setTargetFieldName("fldint")
join.setJoinLayer(joinLayer)
join.setJoinFieldName("y")
join.setUsingMemoryCache(True)
layer.addJoin(join)
join2 = QgsVectorLayerJoinInfo()
join2.setTargetFieldName("fldint")
join2.setJoinLayer(joinLayer2)
join2.setJoinFieldName("y")
join2.setUsingMemoryCache(True)
join2.setPrefix("custom-prefix_")
layer.addJoin(join2)
flds = layer.pendingFields()
self.assertEqual(len(flds), 6)
self.assertEqual(flds[2].name(), "joinlayer_x")
self.assertEqual(flds[3].name(), "joinlayer_z")
self.assertEqual(flds[4].name(), "custom-prefix_x")
self.assertEqual(flds[5].name(), "custom-prefix_z")
self.assertEqual(flds.fieldOrigin(0), QgsFields.OriginProvider)
self.assertEqual(flds.fieldOrigin(2), QgsFields.OriginJoin)
self.assertEqual(flds.fieldOrigin(3), QgsFields.OriginJoin)
self.assertEqual(flds.fieldOriginIndex(0), 0)
self.assertEqual(flds.fieldOriginIndex(2), 0)
self.assertEqual(flds.fieldOriginIndex(3), 2)
f = QgsFeature()
fi = layer.getFeatures()
self.assertTrue(fi.nextFeature(f))
attrs = f.attributes()
self.assertEqual(len(attrs), 6)
self.assertEqual(attrs[0], "test")
self.assertEqual(attrs[1], 123)
self.assertEqual(attrs[2], "foo")
self.assertEqual(attrs[3], 321)
self.assertFalse(fi.nextFeature(f))
f2 = next(layer.getFeatures(QgsFeatureRequest(f.id())))
self.assertEqual(len(f2.attributes()), 6)
self.assertEqual(f2[2], "foo")
self.assertEqual(f2[3], 321)
def test_JoinStats(self):
""" test calculating min/max/uniqueValues on joined field """
joinLayer = createJoinLayer()
layer = createLayerWithTwoPoints()
QgsProject.instance().addMapLayers([joinLayer, layer])
join = QgsVectorLayerJoinInfo()
join.setTargetFieldName("fldint")
join.setJoinLayer(joinLayer)
join.setJoinFieldName("y")
join.setUsingMemoryCache(True)
layer.addJoin(join)
# stats on joined fields should only include values present by join
self.assertEqual(layer.minimumValue(3), 111)
self.assertEqual(layer.maximumValue(3), 321)
self.assertEqual(set(layer.uniqueValues(3)), set([111, 321]))
def test_valid_join_when_opening_project(self):
join_field = "id"
fid = 4
attr_idx = 4
join_attr_idx = 1
new_value = 33.0
# read project and get layers
myPath = os.path.join(unitTestDataPath(), 'joins.qgs')
rc = QgsProject.instance().read(myPath)
layer = QgsProject.instance().mapLayersByName("polys_with_id")[0]
join_layer = QgsProject.instance().mapLayersByName("polys_overlapping_with_id")[0]
# create an attribute table for the main_layer and the
# joined layer
cache = QgsVectorLayerCache(layer, 100)
am = QgsAttributeTableModel(cache)
am.loadLayer()
join_cache = QgsVectorLayerCache(join_layer, 100)
join_am = QgsAttributeTableModel(join_cache)
join_am.loadLayer()
# check feature value of a joined field from the attribute model
model_index = am.idToIndex(fid)
feature_model = am.feature(model_index)
join_model_index = join_am.idToIndex(fid)
join_feature_model = join_am.feature(join_model_index)
self.assertEqual(feature_model.attribute(attr_idx), join_feature_model.attribute(join_attr_idx))
# change attribute value for a feature of the joined layer
join_layer.startEditing()
join_layer.changeAttributeValue(fid, join_attr_idx, new_value)
join_layer.commitChanges()
# check the feature previously modified
join_model_index = join_am.idToIndex(fid)
join_feature_model = join_am.feature(join_model_index)
self.assertEqual(join_feature_model.attribute(join_attr_idx), new_value)
# recreate a new cache and model to simulate the opening of
# a new attribute table
cache = QgsVectorLayerCache(layer, 100)
am = QgsAttributeTableModel(cache)
am.loadLayer()
# test that the model is up to date with the joined layer
model_index = am.idToIndex(fid)
feature_model = am.feature(model_index)
self.assertEqual(feature_model.attribute(attr_idx), new_value)
# restore value
join_layer.startEditing()
join_layer.changeAttributeValue(fid, join_attr_idx, 7.0)
join_layer.commitChanges()
def testUniqueValue(self):
""" test retrieving unique values """
layer = createLayerWithFivePoints()
# test layer with just provider features
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0]))
# add feature with new value
layer.startEditing()
f1 = QgsFeature()
f1.setAttributes(["test2", 999])
self.assertTrue(layer.addFeature(f1))
# should be included in unique values
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999]))
# add it again, should be no change
f2 = QgsFeature()
f2.setAttributes(["test2", 999])
self.assertTrue(layer.addFeature(f1))
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999]))
# add another feature
f3 = QgsFeature()
f3.setAttributes(["test2", 9999])
self.assertTrue(layer.addFeature(f3))
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999, 9999]))
# change an attribute value to a new unique value
f1_id = next(layer.getFeatures()).id()
self.assertTrue(layer.changeAttributeValue(f1_id, 1, 481523))
# note - this isn't 100% accurate, since 123 no longer exists - but it avoids looping through all features
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999, 9999, 481523]))
def testUniqueStringsMatching(self):
""" test retrieving unique strings matching subset """
layer = QgsVectorLayer("Point?field=fldtxt:string", "addfeat", "memory")
pr = layer.dataProvider()
f = QgsFeature()
f.setAttributes(["apple"])
f2 = QgsFeature()
f2.setAttributes(["orange"])
f3 = QgsFeature()
f3.setAttributes(["pear"])
f4 = QgsFeature()
f4.setAttributes(["BanaNa"])
f5 = QgsFeature()
f5.setAttributes(["ApriCot"])
assert pr.addFeatures([f, f2, f3, f4, f5])
assert layer.featureCount() == 5
# test layer with just provider features
self.assertEqual(set(layer.uniqueStringsMatching(0, 'N')), set(['orange', 'BanaNa']))
# add feature with new value
layer.startEditing()
f1 = QgsFeature()
f1.setAttributes(["waterMelon"])
self.assertTrue(layer.addFeature(f1))
# should be included in unique values
self.assertEqual(set(layer.uniqueStringsMatching(0, 'N')), set(['orange', 'BanaNa', 'waterMelon']))
# add it again, should be no change
f2 = QgsFeature()
f2.setAttributes(["waterMelon"])
self.assertTrue(layer.addFeature(f1))
self.assertEqual(set(layer.uniqueStringsMatching(0, 'N')), set(['orange', 'BanaNa', 'waterMelon']))
self.assertEqual(set(layer.uniqueStringsMatching(0, 'aN')), set(['orange', 'BanaNa']))
# add another feature
f3 = QgsFeature()
f3.setAttributes(["pineapple"])
self.assertTrue(layer.addFeature(f3))
self.assertEqual(set(layer.uniqueStringsMatching(0, 'n')), set(['orange', 'BanaNa', 'waterMelon', 'pineapple']))
# change an attribute value to a new unique value
f = QgsFeature()
f1_id = next(layer.getFeatures()).id()
self.assertTrue(layer.changeAttributeValue(f1_id, 0, 'coconut'))
# note - this isn't 100% accurate, since orange no longer exists - but it avoids looping through all features
self.assertEqual(set(layer.uniqueStringsMatching(0, 'n')), set(['orange', 'BanaNa', 'waterMelon', 'pineapple', 'coconut']))
def testMinValue(self):
""" test retrieving minimum values """
layer = createLayerWithFivePoints()
# test layer with just provider features
self.assertEqual(layer.minimumValue(1), -1)
# add feature with new value
layer.startEditing()
f1 = QgsFeature()
f1.setAttributes(["test2", -999])
self.assertTrue(layer.addFeature(f1))
# should be new minimum value
self.assertEqual(layer.minimumValue(1), -999)
# add it again, should be no change
f2 = QgsFeature()
f2.setAttributes(["test2", -999])
self.assertTrue(layer.addFeature(f1))
self.assertEqual(layer.minimumValue(1), -999)
# add another feature
f3 = QgsFeature()
f3.setAttributes(["test2", -1000])
self.assertTrue(layer.addFeature(f3))
self.assertEqual(layer.minimumValue(1), -1000)
# change an attribute value to a new minimum value
f1_id = next(layer.getFeatures()).id()
self.assertTrue(layer.changeAttributeValue(f1_id, 1, -1001))
self.assertEqual(layer.minimumValue(1), -1001)
def testMaxValue(self):
""" test retrieving maximum values """
layer = createLayerWithFivePoints()
# test layer with just provider features
self.assertEqual(layer.maximumValue(1), 888)
# add feature with new value
layer.startEditing()
f1 = QgsFeature()
f1.setAttributes(["test2", 999])
self.assertTrue(layer.addFeature(f1))
# should be new maximum value
self.assertEqual(layer.maximumValue(1), 999)
# add it again, should be no change
f2 = QgsFeature()
f2.setAttributes(["test2", 999])
self.assertTrue(layer.addFeature(f1))
self.assertEqual(layer.maximumValue(1), 999)
# add another feature
f3 = QgsFeature()
f3.setAttributes(["test2", 1000])
self.assertTrue(layer.addFeature(f3))
self.assertEqual(layer.maximumValue(1), 1000)
# change an attribute value to a new maximum value
f1_id = next(layer.getFeatures()).id()
self.assertTrue(layer.changeAttributeValue(f1_id, 1, 1001))
self.assertEqual(layer.maximumValue(1), 1001)
def test_InvalidOperations(self):
layer = createLayerWithOnePoint()
layer.startEditing()
# ADD FEATURE
newF1 = QgsFeature()
self.assertFalse(layer.addFeature(newF1)) # need attributes like the layer has)
# DELETE FEATURE
self.assertFalse(layer.deleteFeature(-333))
# we do not check for existence of the feature id if it's
# not newly added feature
#self.assertFalse(layer.deleteFeature(333))
# CHANGE GEOMETRY
self.assertFalse(layer.changeGeometry(
-333, QgsGeometry.fromPoint(QgsPointXY(1, 1))))
# CHANGE VALUE
self.assertFalse(layer.changeAttributeValue(-333, 0, 1))
self.assertFalse(layer.changeAttributeValue(1, -1, 1))
# ADD ATTRIBUTE
self.assertFalse(layer.addAttribute(QgsField()))
# DELETE ATTRIBUTE
self.assertFalse(layer.deleteAttribute(-1))
def onBlendModeChanged(self, mode):
self.blendModeTest = mode
def test_setBlendMode(self):
layer = createLayerWithOnePoint()
self.blendModeTest = 0
layer.blendModeChanged.connect(self.onBlendModeChanged)
layer.setBlendMode(QPainter.CompositionMode_Screen)
self.assertEqual(self.blendModeTest, QPainter.CompositionMode_Screen)
self.assertEqual(layer.blendMode(), QPainter.CompositionMode_Screen)
def test_setFeatureBlendMode(self):
layer = createLayerWithOnePoint()
self.blendModeTest = 0
layer.featureBlendModeChanged.connect(self.onBlendModeChanged)
layer.setFeatureBlendMode(QPainter.CompositionMode_Screen)
self.assertEqual(self.blendModeTest, QPainter.CompositionMode_Screen)
self.assertEqual(layer.featureBlendMode(), QPainter.CompositionMode_Screen)
def test_ExpressionField(self):
layer = createLayerWithOnePoint()
cnt = layer.pendingFields().count()
idx = layer.addExpressionField('5', QgsField('test', QVariant.LongLong))
fet = next(layer.getFeatures())
self.assertEqual(fet[idx], 5)
# check fields
self.assertEqual(layer.fields().count(), cnt + 1)
self.assertEqual(fet.fields(), layer.fields())
# retrieve single feature and check fields
fet = next(layer.getFeatures(QgsFeatureRequest().setFilterFid(1)))
self.assertEqual(fet.fields(), layer.fields())
layer.updateExpressionField(idx, '9')
self.assertEqual(next(layer.getFeatures())[idx], 9)
layer.removeExpressionField(idx)
self.assertEqual(layer.pendingFields().count(), cnt)
# expression field which references itself
idx = layer.addExpressionField('sum(test2)', QgsField('test2', QVariant.LongLong))
fet = next(layer.getFeatures())
self.assertEqual(fet['test2'], NULL)
def test_ExpressionFieldEllipsoidLengthCalculation(self):
#create a temporary layer
temp_layer = QgsVectorLayer("LineString?crs=epsg:3111&field=pk:int", "vl", "memory")
self.assertTrue(temp_layer.isValid())
f1 = QgsFeature(temp_layer.dataProvider().fields(), 1)
f1.setAttribute("pk", 1)
f1.setGeometry(QgsGeometry.fromPolylineXY([QgsPointXY(2484588, 2425722), QgsPointXY(2482767, 2398853)]))
temp_layer.dataProvider().addFeatures([f1])
# set project CRS and ellipsoid
srs = QgsCoordinateReferenceSystem(3111, QgsCoordinateReferenceSystem.EpsgCrsId)
QgsProject.instance().setCrs(srs)
QgsProject.instance().setEllipsoid("WGS84")
QgsProject.instance().setDistanceUnits(QgsUnitTypes.DistanceMeters)
idx = temp_layer.addExpressionField('$length', QgsField('length', QVariant.Double)) # NOQA
# check value
f = next(temp_layer.getFeatures())
expected = 26932.156
self.assertAlmostEqual(f['length'], expected, 3)
# change project length unit, check calculation respects unit
QgsProject.instance().setDistanceUnits(QgsUnitTypes.DistanceFeet)
f = next(temp_layer.getFeatures())
expected = 88360.0918635
self.assertAlmostEqual(f['length'], expected, 3)
def test_ExpressionFieldEllipsoidAreaCalculation(self):
#create a temporary layer
temp_layer = QgsVectorLayer("Polygon?crs=epsg:3111&field=pk:int", "vl", "memory")
self.assertTrue(temp_layer.isValid())
f1 = QgsFeature(temp_layer.dataProvider().fields(), 1)
f1.setAttribute("pk", 1)
f1.setGeometry(QgsGeometry.fromPolygon([[QgsPointXY(2484588, 2425722), QgsPointXY(2482767, 2398853), QgsPointXY(2520109, 2397715), QgsPointXY(2520792, 2425494), QgsPointXY(2484588, 2425722)]]))
temp_layer.dataProvider().addFeatures([f1])
# set project CRS and ellipsoid
srs = QgsCoordinateReferenceSystem(3111, QgsCoordinateReferenceSystem.EpsgCrsId)
QgsProject.instance().setCrs(srs)
QgsProject.instance().setEllipsoid("WGS84")
QgsProject.instance().setAreaUnits(QgsUnitTypes.AreaSquareMeters)
idx = temp_layer.addExpressionField('$area', QgsField('area', QVariant.Double)) # NOQA
# check value
f = next(temp_layer.getFeatures())
expected = 1009089817.0
self.assertAlmostEqual(f['area'], expected, delta=1.0)
# change project area unit, check calculation respects unit
QgsProject.instance().setAreaUnits(QgsUnitTypes.AreaSquareMiles)
f = next(temp_layer.getFeatures())
expected = 389.6117565069
self.assertAlmostEqual(f['area'], expected, 3)
def test_ExpressionFilter(self):
layer = createLayerWithOnePoint()
idx = layer.addExpressionField('5', QgsField('test', QVariant.LongLong)) # NOQA
features = layer.getFeatures(QgsFeatureRequest().setFilterExpression('"test" = 6'))
assert(len(list(features)) == 0)
features = layer.getFeatures(QgsFeatureRequest().setFilterExpression('"test" = 5'))
assert(len(list(features)) == 1)
def testSelectByIds(self):
""" Test selecting by ID"""
layer = QgsVectorLayer(os.path.join(unitTestDataPath(), 'points.shp'), 'Points', 'ogr')
# SetSelection
layer.selectByIds([1, 3, 5, 7], QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([1, 3, 5, 7]))
# check that existing selection is cleared
layer.selectByIds([2, 4, 6], QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 4, 6]))
# AddToSelection
layer.selectByIds([3, 5], QgsVectorLayer.AddToSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3, 4, 5, 6]))
layer.selectByIds([1], QgsVectorLayer.AddToSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([1, 2, 3, 4, 5, 6]))
# IntersectSelection
layer.selectByIds([1, 3, 5, 6], QgsVectorLayer.IntersectSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([1, 3, 5, 6]))
layer.selectByIds([1, 2, 5, 6], QgsVectorLayer.IntersectSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([1, 5, 6]))
# RemoveFromSelection
layer.selectByIds([2, 6, 7], QgsVectorLayer.RemoveFromSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([1, 5]))
layer.selectByIds([1, 5], QgsVectorLayer.RemoveFromSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([]))
def testSelectByExpression(self):
""" Test selecting by expression """
layer = QgsVectorLayer(os.path.join(unitTestDataPath(), 'points.shp'), 'Points', 'ogr')
# SetSelection
layer.selectByExpression('"Class"=\'B52\' and "Heading" > 10 and "Heading" <70', QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([10, 11]))
# check that existing selection is cleared
layer.selectByExpression('"Class"=\'Biplane\'', QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([1, 5, 6, 7, 8]))
# SetSelection no matching
layer.selectByExpression('"Class"=\'A380\'', QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([]))
# AddToSelection
layer.selectByExpression('"Importance"=3', QgsVectorLayer.AddToSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([0, 2, 3, 4, 14]))
layer.selectByExpression('"Importance"=4', QgsVectorLayer.AddToSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([0, 2, 3, 4, 13, 14]))
# IntersectSelection
layer.selectByExpression('"Heading"<100', QgsVectorLayer.IntersectSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([0, 2, 3, 4]))
layer.selectByExpression('"Cabin Crew"=1', QgsVectorLayer.IntersectSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3]))
# RemoveFromSelection
layer.selectByExpression('"Heading"=85', QgsVectorLayer.RemoveFromSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([3]))
layer.selectByExpression('"Heading"=95', QgsVectorLayer.RemoveFromSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([]))
def testSelectByRect(self):
""" Test selecting by rectangle """
layer = QgsVectorLayer(os.path.join(unitTestDataPath(), 'points.shp'), 'Points', 'ogr')
# SetSelection
layer.selectByRect(QgsRectangle(-112, 30, -94, 45), QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3, 7, 10, 11, 15]))
# check that existing selection is cleared
layer.selectByRect(QgsRectangle(-112, 30, -94, 37), QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3, 10, 15]))
# SetSelection no matching
layer.selectByRect(QgsRectangle(112, 30, 115, 45), QgsVectorLayer.SetSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([]))
# AddToSelection
layer.selectByRect(QgsRectangle(-112, 30, -94, 37), QgsVectorLayer.AddToSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3, 10, 15]))
layer.selectByRect(QgsRectangle(-112, 37, -94, 45), QgsVectorLayer.AddToSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3, 7, 10, 11, 15]))
# IntersectSelection
layer.selectByRect(QgsRectangle(-112, 30, -94, 37), QgsVectorLayer.IntersectSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 3, 10, 15]))
layer.selectByIds([2, 10, 13])
layer.selectByRect(QgsRectangle(-112, 30, -94, 37), QgsVectorLayer.IntersectSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([2, 10]))
# RemoveFromSelection
layer.selectByRect(QgsRectangle(-112, 30, -94, 45), QgsVectorLayer.SetSelection)
layer.selectByRect(QgsRectangle(-112, 30, -94, 37), QgsVectorLayer.RemoveFromSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([7, 11]))
layer.selectByRect(QgsRectangle(-112, 30, -94, 45), QgsVectorLayer.RemoveFromSelection)
self.assertEqual(set(layer.selectedFeatureIds()), set([]))
def testAggregate(self):
""" Test aggregate calculation """
layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
pr = layer.dataProvider()
int_values = [4, 2, 3, 2, 5, None, 8]
features = []
for i in int_values:
f = QgsFeature()
f.setFields(layer.fields())
f.setAttributes([i])
features.append(f)
assert pr.addFeatures(features)
tests = [[QgsAggregateCalculator.Count, 6],
[QgsAggregateCalculator.Sum, 24],
[QgsAggregateCalculator.Mean, 4],
[QgsAggregateCalculator.StDev, 2.0816],
[QgsAggregateCalculator.StDevSample, 2.2803],
[QgsAggregateCalculator.Min, 2],
[QgsAggregateCalculator.Max, 8],
[QgsAggregateCalculator.Range, 6],
[QgsAggregateCalculator.Median, 3.5],
[QgsAggregateCalculator.CountDistinct, 5],
[QgsAggregateCalculator.CountMissing, 1],
[QgsAggregateCalculator.FirstQuartile, 2],
[QgsAggregateCalculator.ThirdQuartile, 5.0],
[QgsAggregateCalculator.InterQuartileRange, 3.0]
]
for t in tests:
val, ok = layer.aggregate(t[0], 'fldint')
self.assertTrue(ok)
if isinstance(t[1], int):
self.assertEqual(val, t[1])
else:
self.assertAlmostEqual(val, t[1], 3)
# test with parameters
layer = QgsVectorLayer("Point?field=fldstring:string", "layer", "memory")
pr = layer.dataProvider()
string_values = ['this', 'is', 'a', 'test']
features = []
for s in string_values:
f = QgsFeature()
f.setFields(layer.fields())
f.setAttributes([s])
features.append(f)
assert pr.addFeatures(features)
params = QgsAggregateCalculator.AggregateParameters()
params.delimiter = ' '
val, ok = layer.aggregate(QgsAggregateCalculator.StringConcatenate, 'fldstring', params)
self.assertTrue(ok)
self.assertEqual(val, 'this is a test')
def testAggregateInVirtualField(self):
"""
Test aggregates in a virtual field
"""
layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
pr = layer.dataProvider()
int_values = [4, 2, 3, 2, 5, None, 8]
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.Double)
layer.addExpressionField('sum(fldint*2)', field)
vals = [f['virtual'] for f in layer.getFeatures()]
self.assertEqual(vals, [48, 48, 48, 48, 48, 48, 48])
def onLayerOpacityChanged(self, tr):
self.opacityTest = tr
def test_setLayerOpacity(self):
layer = createLayerWithOnePoint()
self.opacityTest = 0
layer.opacityChanged.connect(self.onLayerOpacityChanged)
layer.setOpacity(0.5)
self.assertEqual(self.opacityTest, 0.5)
self.assertEqual(layer.opacity(), 0.5)
def onRendererChanged(self):
self.rendererChanged = True
def test_setRenderer(self):
layer = createLayerWithOnePoint()
self.rendererChanged = False
layer.rendererChanged.connect(self.onRendererChanged)
r = QgsSingleSymbolRenderer(QgsSymbol.defaultSymbol(QgsWkbTypes.PointGeometry))
layer.setRenderer(r)
self.assertTrue(self.rendererChanged)
self.assertEqual(layer.renderer(), r)
def testGetSetAliases(self):
""" test getting and setting aliases """
layer = createLayerWithOnePoint()
self.assertFalse(layer.attributeAlias(0))
self.assertFalse(layer.attributeAlias(1))
self.assertFalse(layer.attributeAlias(2))
layer.setFieldAlias(0, "test")
self.assertEqual(layer.attributeAlias(0), "test")
self.assertFalse(layer.attributeAlias(1))
self.assertFalse(layer.attributeAlias(2))
self.assertEqual(layer.fields().at(0).alias(), "test")
layer.setFieldAlias(1, "test2")
self.assertEqual(layer.attributeAlias(0), "test")
self.assertEqual(layer.attributeAlias(1), "test2")
self.assertFalse(layer.attributeAlias(2))
self.assertEqual(layer.fields().at(0).alias(), "test")
self.assertEqual(layer.fields().at(1).alias(), "test2")
layer.setFieldAlias(1, None)
self.assertEqual(layer.attributeAlias(0), "test")
self.assertFalse(layer.attributeAlias(1))
self.assertFalse(layer.attributeAlias(2))
self.assertEqual(layer.fields().at(0).alias(), "test")
self.assertFalse(layer.fields().at(1).alias())
layer.removeFieldAlias(0)
self.assertFalse(layer.attributeAlias(0))
self.assertFalse(layer.attributeAlias(1))
self.assertFalse(layer.attributeAlias(2))
self.assertFalse(layer.fields().at(0).alias())
self.assertFalse(layer.fields().at(1).alias())
def testSaveRestoreAliases(self):
""" test saving and restoring aliases from xml"""
layer = createLayerWithOnePoint()
# no default expressions
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer2 = createLayerWithOnePoint()
self.assertTrue(layer2.readXml(elem, QgsReadWriteContext()))
self.assertFalse(layer2.attributeAlias(0))
self.assertFalse(layer2.attributeAlias(1))
# set some aliases
layer.setFieldAlias(0, "test")
layer.setFieldAlias(1, "test2")
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer3 = createLayerWithOnePoint()
self.assertTrue(layer3.readXml(elem, QgsReadWriteContext()))
self.assertEqual(layer3.attributeAlias(0), "test")
self.assertEqual(layer3.attributeAlias(1), "test2")
self.assertEqual(layer3.fields().at(0).alias(), "test")
self.assertEqual(layer3.fields().at(1).alias(), "test2")
def testGetSetDefaults(self):
""" test getting and setting default expressions """
layer = createLayerWithOnePoint()
self.assertFalse(layer.defaultValueDefinition(0))
self.assertFalse(layer.defaultValueDefinition(0).expression())
self.assertFalse(layer.defaultValueDefinition(0).applyOnUpdate())
self.assertFalse(layer.defaultValueDefinition(1))
self.assertFalse(layer.defaultValueDefinition(2))
layer.setDefaultValueDefinition(0, QgsDefaultValue("'test'"))
self.assertTrue(layer.defaultValueDefinition(0))
self.assertEqual(layer.defaultValueDefinition(0).expression(), "'test'")
self.assertFalse(layer.defaultValueDefinition(1))
self.assertFalse(layer.defaultValueDefinition(2))
self.assertEqual(layer.fields().at(0).defaultValueDefinition().expression(), "'test'")
layer.setDefaultValueDefinition(1, QgsDefaultValue("2+2"))
self.assertEqual(layer.defaultValueDefinition(0).expression(), "'test'")
self.assertEqual(layer.defaultValueDefinition(1).expression(), "2+2")
self.assertFalse(layer.defaultValueDefinition(2))
self.assertEqual(layer.fields().at(0).defaultValueDefinition().expression(), "'test'")
self.assertEqual(layer.fields().at(1).defaultValueDefinition().expression(), "2+2")
def testSaveRestoreDefaults(self):
""" test saving and restoring default expressions from xml"""
layer = createLayerWithOnePoint()
# no default expressions
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer2 = createLayerWithOnePoint()
self.assertTrue(layer2.readXml(elem, QgsReadWriteContext()))
self.assertFalse(layer2.defaultValueDefinition(0))
self.assertFalse(layer2.defaultValueDefinition(1))
# set some default expressions
layer.setDefaultValueDefinition(0, QgsDefaultValue("'test'"))
layer.setDefaultValueDefinition(1, QgsDefaultValue("2+2"))
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer3 = createLayerWithOnePoint()
self.assertTrue(layer3.readXml(elem, QgsReadWriteContext()))
self.assertEqual(layer3.defaultValueDefinition(0).expression(), "'test'")
self.assertEqual(layer3.defaultValueDefinition(1).expression(), "2+2")
self.assertEqual(layer3.fields().at(0).defaultValueDefinition().expression(), "'test'")
self.assertEqual(layer3.fields().at(1).defaultValueDefinition().expression(), "2+2")
def testEvaluatingDefaultExpressions(self):
""" tests calculation of default values"""
layer = createLayerWithOnePoint()
layer.setDefaultValueDefinition(0, QgsDefaultValue("'test'"))
layer.setDefaultValueDefinition(1, QgsDefaultValue("2+2"))
self.assertEqual(layer.defaultValue(0), 'test')
self.assertEqual(layer.defaultValue(1), 4)
# using feature
layer.setDefaultValueDefinition(1, QgsDefaultValue('$id * 2'))
feature = QgsFeature(4)
feature.setValid(True)
feature.setFields(layer.fields())
# no feature:
self.assertFalse(layer.defaultValue(1))
# with feature:
self.assertEqual(layer.defaultValue(0, feature), 'test')
self.assertEqual(layer.defaultValue(1, feature), 8)
# using feature geometry
layer.setDefaultValueDefinition(1, QgsDefaultValue('$x * 2'))
feature.setGeometry(QgsGeometry(QgsPoint(6, 7)))
self.assertEqual(layer.defaultValue(1, feature), 12)
# using contexts
scope = QgsExpressionContextScope()
scope.setVariable('var1', 16)
context = QgsExpressionContext()
context.appendScope(scope)
layer.setDefaultValueDefinition(1, QgsDefaultValue('$id + @var1'))
self.assertEqual(layer.defaultValue(1, feature, context), 20)
# if no scope passed, should use a default constructed one including layer variables
QgsExpressionContextUtils.setLayerVariable(layer, 'var2', 4)
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), 'var3', 8)
layer.setDefaultValueDefinition(1, QgsDefaultValue('to_int(@var2) + to_int(@var3) + $id'))
self.assertEqual(layer.defaultValue(1, feature), 16)
# bad expression
layer.setDefaultValueDefinition(1, QgsDefaultValue('not a valid expression'))
self.assertFalse(layer.defaultValue(1))
def testApplyOnUpdateDefaultExpressions(self):
"""tests apply on update of default values"""
layer = createLayerWithOnePoint()
layer.setDefaultValueDefinition(0, QgsDefaultValue("CONCAT('l: ', @number, ',f: ', \"fldint\" )", True))
layer.setDefaultValueDefinition(1, QgsDefaultValue("1 * @number", False))
QgsExpressionContextUtils.setLayerVariable(layer, 'number', 4)
layer.startEditing()
feature = QgsFeature()
feature.setFields(layer.fields())
feature.setValid(True)
# Both default values should be set on feature create
feature.setAttribute(1, layer.defaultValue(1, feature))
feature.setAttribute(0, layer.defaultValue(0, feature))
self.assertTrue(layer.addFeature(feature))
fid = feature.id()
self.assertEqual(layer.getFeature(fid)['fldtxt'], 'l: 4,f: 4')
self.assertEqual(layer.getFeature(fid)['fldint'], 4)
# ApplyOnUpdateDefaultValue should be set on changeAttributeValue
layer.changeAttributeValue(fid, 1, 20)
self.assertEqual(layer.getFeature(fid)['fldtxt'], 'l: 4,f: 20')
self.assertEqual(layer.getFeature(fid)['fldint'], 20)
# When changing the value of the "derived" attribute, only this one
# should be updated
QgsExpressionContextUtils.setLayerVariable(layer, 'number', 8)
layer.changeAttributeValue(fid, 0, 0)
self.assertEqual(layer.getFeature(fid)['fldtxt'], 'l: 8,f: 20')
self.assertEqual(layer.getFeature(fid)['fldint'], 20)
# Check update on geometry change
layer.setDefaultValueDefinition(1, QgsDefaultValue("x($geometry)", True))
layer.changeGeometry(fid, QgsGeometry.fromPoint(QgsPointXY(300, 200)))
self.assertEqual(layer.getFeature(fid)['fldint'], 300)
def testGetSetConstraints(self):
""" test getting and setting field constraints """
layer = createLayerWithOnePoint()
self.assertFalse(layer.fieldConstraints(0))
self.assertFalse(layer.fieldConstraints(1))
self.assertFalse(layer.fieldConstraints(2))
layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer.fieldConstraints(0), QgsFieldConstraints.ConstraintNotNull)
self.assertFalse(layer.fieldConstraints(1))
self.assertFalse(layer.fieldConstraints(2))
self.assertEqual(layer.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(0).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthHard)
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
self.assertEqual(layer.fieldConstraints(0), QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer.fieldConstraints(1), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique)
self.assertFalse(layer.fieldConstraints(2))
self.assertEqual(layer.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(0).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthHard)
self.assertEqual(layer.fields().at(1).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique)
self.assertEqual(layer.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintUnique),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(1).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthHard)
self.assertEqual(layer.fields().at(1).constraints().constraintStrength(QgsFieldConstraints.ConstraintUnique),
QgsFieldConstraints.ConstraintStrengthHard)
layer.removeFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
layer.removeFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
self.assertEqual(layer.fieldConstraints(0), QgsFieldConstraints.ConstraintNotNull)
self.assertFalse(layer.fieldConstraints(1))
self.assertFalse(layer.fieldConstraints(2))
self.assertEqual(layer.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(0).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthHard)
self.assertFalse(layer.fields().at(1).constraints().constraints())
self.assertEqual(layer.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginNotSet)
self.assertEqual(layer.fields().at(1).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthNotSet)
def testSaveRestoreConstraints(self):
""" test saving and restoring constraints from xml"""
layer = createLayerWithOnePoint()
# no constraints
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer2 = createLayerWithOnePoint()
self.assertTrue(layer2.readXml(elem, QgsReadWriteContext()))
self.assertFalse(layer2.fieldConstraints(0))
self.assertFalse(layer2.fieldConstraints(1))
# set some constraints
layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintNotNull)
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull, QgsFieldConstraints.ConstraintStrengthSoft)
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer3 = createLayerWithOnePoint()
self.assertTrue(layer3.readXml(elem, QgsReadWriteContext()))
self.assertEqual(layer3.fieldConstraints(0), QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer3.fieldConstraints(1), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique)
self.assertEqual(layer3.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull)
self.assertEqual(layer3.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(0).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthHard)
self.assertEqual(layer3.fields().at(1).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique)
self.assertEqual(layer3.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer3.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintUnique),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer.fields().at(1).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull),
QgsFieldConstraints.ConstraintStrengthSoft)
self.assertEqual(layer.fields().at(1).constraints().constraintStrength(QgsFieldConstraints.ConstraintUnique),
QgsFieldConstraints.ConstraintStrengthHard)
def testGetSetConstraintExpressions(self):
""" test getting and setting field constraint expressions """
layer = createLayerWithOnePoint()
self.assertFalse(layer.constraintExpression(0))
self.assertFalse(layer.constraintExpression(1))
self.assertFalse(layer.constraintExpression(2))
layer.setConstraintExpression(0, '1+2')
self.assertEqual(layer.constraintExpression(0), '1+2')
self.assertFalse(layer.constraintExpression(1))
self.assertFalse(layer.constraintExpression(2))
self.assertEqual(layer.fields().at(0).constraints().constraintExpression(), '1+2')
layer.setConstraintExpression(1, '3+4', 'desc')
self.assertEqual(layer.constraintExpression(0), '1+2')
self.assertEqual(layer.constraintExpression(1), '3+4')
self.assertEqual(layer.constraintDescription(1), 'desc')
self.assertFalse(layer.constraintExpression(2))
self.assertEqual(layer.fields().at(0).constraints().constraintExpression(), '1+2')
self.assertEqual(layer.fields().at(1).constraints().constraintExpression(), '3+4')
self.assertEqual(layer.fields().at(1).constraints().constraintDescription(), 'desc')
layer.setConstraintExpression(1, None)
self.assertEqual(layer.constraintExpression(0), '1+2')
self.assertFalse(layer.constraintExpression(1))
self.assertFalse(layer.constraintExpression(2))
self.assertEqual(layer.fields().at(0).constraints().constraintExpression(), '1+2')
self.assertFalse(layer.fields().at(1).constraints().constraintExpression())
def testSaveRestoreConstraintExpressions(self):
""" test saving and restoring constraint expressions from xml"""
layer = createLayerWithOnePoint()
# no constraints
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer2 = createLayerWithOnePoint()
self.assertTrue(layer2.readXml(elem, QgsReadWriteContext()))
self.assertFalse(layer2.constraintExpression(0))
self.assertFalse(layer2.constraintExpression(1))
# set some constraints
layer.setConstraintExpression(0, '1+2')
layer.setConstraintExpression(1, '3+4', 'desc')
doc = QDomDocument("testdoc")
elem = doc.createElement("maplayer")
self.assertTrue(layer.writeXml(elem, doc, QgsReadWriteContext()))
layer3 = createLayerWithOnePoint()
self.assertTrue(layer3.readXml(elem, QgsReadWriteContext()))
self.assertEqual(layer3.constraintExpression(0), '1+2')
self.assertEqual(layer3.constraintExpression(1), '3+4')
self.assertEqual(layer3.constraintDescription(1), 'desc')
self.assertEqual(layer3.fields().at(0).constraints().constraintExpression(), '1+2')
self.assertEqual(layer3.fields().at(1).constraints().constraintExpression(), '3+4')
self.assertEqual(layer3.fields().at(1).constraints().constraintDescription(), 'desc')
self.assertEqual(layer3.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintExpression)
self.assertEqual(layer3.fields().at(1).constraints().constraints(), QgsFieldConstraints.ConstraintExpression)
self.assertEqual(layer3.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintExpression),
QgsFieldConstraints.ConstraintOriginLayer)
self.assertEqual(layer3.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintExpression),
QgsFieldConstraints.ConstraintOriginLayer)
def testGetFeatureLimitWithEdits(self):
""" test getting features with a limit, when edits are present """
layer = createLayerWithOnePoint()
# now has one feature with id 0
pr = layer.dataProvider()
f1 = QgsFeature(1)
f1.setAttributes(["test", 3])
f1.setGeometry(QgsGeometry.fromPoint(QgsPointXY(300, 200)))
f2 = QgsFeature(2)
f2.setAttributes(["test", 3])
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
f3 = QgsFeature(3)
f3.setAttributes(["test", 3])
f3.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
self.assertTrue(pr.addFeatures([f1, f2, f3]))
req = QgsFeatureRequest().setLimit(2)
self.assertEqual(len(list(layer.getFeatures(req))), 2)
# now delete feature f1
layer.startEditing()
self.assertTrue(layer.deleteFeature(1))
req = QgsFeatureRequest().setLimit(2)
self.assertEqual(len(list(layer.getFeatures(req))), 2)
layer.rollBack()
# change an attribute value required by filter
layer.startEditing()
req = QgsFeatureRequest().setFilterExpression('fldint=3').setLimit(2)
self.assertTrue(layer.changeAttributeValue(2, 1, 4))
self.assertEqual(len(list(layer.getFeatures(req))), 2)
layer.rollBack()
layer.startEditing()
req = QgsFeatureRequest().setFilterRect(QgsRectangle(50, 100, 150, 300)).setLimit(2)
self.assertTrue(layer.changeGeometry(2, QgsGeometry.fromPoint(QgsPointXY(500, 600))))
self.assertEqual(len(list(layer.getFeatures(req))), 2)
layer.rollBack()
def testClone(self):
# init crs
srs = QgsCoordinateReferenceSystem(3111, QgsCoordinateReferenceSystem.EpsgCrsId)
# init map layer styles
tmplayer = createLayerWithTwoPoints()
sym1 = QgsLineSymbol()
sym1.setColor(Qt.magenta)
tmplayer.setRenderer(QgsSingleSymbolRenderer(sym1))
style0 = QgsMapLayerStyle()
style0.readFromLayer(tmplayer)
style1 = QgsMapLayerStyle()
style1.readFromLayer(tmplayer)
# init dependencies layers
ldep = createLayerWithTwoPoints()
dep = QgsMapLayerDependency(ldep.id())
# init layer
layer = createLayerWithTwoPoints()
layer.setBlendMode(QPainter.CompositionMode_Screen)
layer.styleManager().addStyle('style0', style0)
layer.styleManager().addStyle('style1', style1)
layer.setName('MyName')
layer.setShortName('MyShortName')
layer.setMaximumScale(0.5)
layer.setMinimumScale(1.5)
layer.setScaleBasedVisibility(True)
layer.setTitle('MyTitle')
layer.setAbstract('MyAbstract')
layer.setKeywordList('MyKeywordList')
layer.setDataUrl('MyDataUrl')
layer.setDataUrlFormat('MyDataUrlFormat')
layer.setAttribution('MyAttribution')
layer.setAttributionUrl('MyAttributionUrl')
layer.setMetadataUrl('MyMetadataUrl')
layer.setMetadataUrlType('MyMetadataUrlType')
layer.setMetadataUrlFormat('MyMetadataUrlFormat')
layer.setLegendUrl('MyLegendUrl')
layer.setLegendUrlFormat('MyLegendUrlFormat')
layer.setDependencies([dep])
layer.setCrs(srs)
layer.setCustomProperty('MyKey0', 'MyValue0')
layer.setCustomProperty('MyKey1', 'MyValue1')
layer.setOpacity(0.66)
layer.setProviderEncoding('latin9')
layer.setDisplayExpression('MyDisplayExpression')
layer.setMapTipTemplate('MyMapTipTemplate')
layer.setExcludeAttributesWfs(['MyExcludeAttributeWFS'])
layer.setExcludeAttributesWms(['MyExcludeAttributeWMS'])
layer.setFeatureBlendMode(QPainter.CompositionMode_Xor)
sym = QgsLineSymbol()
sym.setColor(Qt.magenta)
layer.setRenderer(QgsSingleSymbolRenderer(sym))
simplify = layer.simplifyMethod()
simplify.setTolerance(33.3)
simplify.setThreshold(0.333)
layer.setSimplifyMethod(simplify)
layer.setFieldAlias(0, 'MyAlias0')
layer.setFieldAlias(1, 'MyAlias1')
jl0 = createLayerWithTwoPoints()
j0 = QgsVectorLayerJoinInfo()
j0.setJoinLayer(jl0)
jl1 = createLayerWithTwoPoints()
j1 = QgsVectorLayerJoinInfo()
j1.setJoinLayer(jl1)
layer.addJoin(j0)
layer.addJoin(j1)
fids = layer.allFeatureIds()
selected_fids = fids[0:3]
layer.selectByIds(selected_fids)
cfg = layer.attributeTableConfig()
cfg.setSortOrder(Qt.DescendingOrder) # by default AscendingOrder
layer.setAttributeTableConfig(cfg)
pal = QgsPalLayerSettings()
text_format = QgsTextFormat()
text_format.setSize(33)
text_format.setColor(Qt.magenta)
pal.setFormat(text_format)
labeling = QgsVectorLayerSimpleLabeling(pal)
layer.setLabeling(labeling)
diag_renderer = QgsSingleCategoryDiagramRenderer()
diag_renderer.setAttributeLegend(False) # true by default
layer.setDiagramRenderer(diag_renderer)
diag_settings = QgsDiagramLayerSettings()
diag_settings.setPriority(3)
diag_settings.setZIndex(0.33)
layer.setDiagramLayerSettings(diag_settings)
edit_form_config = layer.editFormConfig()
edit_form_config.setUiForm("MyUiForm")
edit_form_config.setInitFilePath("MyInitFilePath")
layer.setEditFormConfig(edit_form_config)
widget_setup = QgsEditorWidgetSetup("MyWidgetSetupType", {})
layer.setEditorWidgetSetup(0, widget_setup)
layer.setConstraintExpression(0, "MyFieldConstraintExpression")
layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique, QgsFieldConstraints.ConstraintStrengthHard)
layer.setDefaultValueDefinition(0, QgsDefaultValue("MyDefaultValueExpression"))
action = QgsAction(QgsAction.Unix, "MyActionDescription", "MyActionCmd")
layer.actions().addAction(action)
# clone layer
clone = layer.clone()
# generate xml from layer
layer_doc = QDomDocument("doc")
layer_elem = layer_doc.createElement("maplayer")
layer.writeLayerXml(layer_elem, layer_doc, QgsReadWriteContext())
# generate xml from clone
clone_doc = QDomDocument("doc")
clone_elem = clone_doc.createElement("maplayer")
clone.writeLayerXml(clone_elem, clone_doc, QgsReadWriteContext())
# replace id within xml of clone
clone_id_elem = clone_elem.firstChildElement("id")
clone_id_elem_patch = clone_doc.createElement("id")
clone_id_elem_patch_value = clone_doc.createTextNode(layer.id())
clone_id_elem_patch.appendChild(clone_id_elem_patch_value)
clone_elem.replaceChild(clone_id_elem_patch, clone_id_elem)
# update doc
clone_doc.appendChild(clone_elem)
layer_doc.appendChild(layer_elem)
# compare xml documents
self.assertEqual(layer_doc.toString(), clone_doc.toString())
def testQgsVectorLayerSelectedFeatureSource(self):
"""
test QgsVectorLayerSelectedFeatureSource
"""
layer = QgsVectorLayer("Point?crs=epsg:3111&field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f1 = QgsFeature(1)
f1.setAttributes(["test", 123])
f1.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
f2 = QgsFeature(2)
f2.setAttributes(["test2", 457])
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(200, 200)))
f3 = QgsFeature(3)
f3.setAttributes(["test2", 888])
f3.setGeometry(QgsGeometry.fromPoint(QgsPointXY(300, 200)))
f4 = QgsFeature(4)
f4.setAttributes(["test3", -1])
f4.setGeometry(QgsGeometry.fromPoint(QgsPointXY(400, 300)))
f5 = QgsFeature(5)
f5.setAttributes(["test4", 0])
f5.setGeometry(QgsGeometry.fromPoint(QgsPointXY(0, 0)))
self.assertTrue(pr.addFeatures([f1, f2, f3, f4, f5]))
self.assertEqual(layer.featureCount(), 5)
source = QgsVectorLayerSelectedFeatureSource(layer)
self.assertEqual(source.sourceCrs().authid(), 'EPSG:3111')
self.assertEqual(source.wkbType(), QgsWkbTypes.Point)
self.assertEqual(source.fields(), layer.fields())
# no selection
self.assertEqual(source.featureCount(), 0)
it = source.getFeatures()
f = QgsFeature()
self.assertFalse(it.nextFeature(f))
# with selection
layer.selectByIds([f1.id(), f3.id(), f5.id()])
source = QgsVectorLayerSelectedFeatureSource(layer)
self.assertEqual(source.featureCount(), 3)
ids = set([f.id() for f in source.getFeatures()])
self.assertEqual(ids, {f1.id(), f3.id(), f5.id()})
# test that requesting subset of ids intersects this request with the selected ids
ids = set([f.id() for f in source.getFeatures(QgsFeatureRequest().setFilterFids([f1.id(), f2.id(), f5.id()]))])
self.assertEqual(ids, {f1.id(), f5.id()})
# test that requesting id works
ids = set([f.id() for f in source.getFeatures(QgsFeatureRequest().setFilterFid(f1.id()))])
self.assertEqual(ids, {f1.id()})
ids = set([f.id() for f in source.getFeatures(QgsFeatureRequest().setFilterFid(f5.id()))])
self.assertEqual(ids, {f5.id()})
# test that source has stored snapshot of selected features
layer.selectByIds([f2.id(), f4.id()])
self.assertEqual(source.featureCount(), 3)
ids = set([f.id() for f in source.getFeatures()])
self.assertEqual(ids, {f1.id(), f3.id(), f5.id()})
# test that source is not dependent on layer
del layer
ids = set([f.id() for f in source.getFeatures()])
self.assertEqual(ids, {f1.id(), f3.id(), f5.id()})
def testFeatureRequestWithReprojectionAndVirtualFields(self):
layer = self.getSource()
field = QgsField('virtual', QVariant.Double)
layer.addExpressionField('$x', field)
virtual_values = [f['virtual'] for f in layer.getFeatures()]
self.assertAlmostEqual(virtual_values[0], -71.123, 2)
self.assertEqual(virtual_values[1], NULL)
self.assertAlmostEqual(virtual_values[2], -70.332, 2)
self.assertAlmostEqual(virtual_values[3], -68.2, 2)
self.assertAlmostEqual(virtual_values[4], -65.32, 2)
# repeat, with reprojection on request
request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785'))
features = [f for f in layer.getFeatures(request)]
# virtual field value should not change, even though geometry has
self.assertAlmostEqual(features[0]['virtual'], -71.123, 2)
self.assertAlmostEqual(features[0].geometry().constGet().x(), -7917376, -5)
self.assertEqual(features[1]['virtual'], NULL)
self.assertFalse(features[1].hasGeometry())
self.assertAlmostEqual(features[2]['virtual'], -70.332, 2)
self.assertAlmostEqual(features[2].geometry().constGet().x(), -7829322, -5)
self.assertAlmostEqual(features[3]['virtual'], -68.2, 2)
self.assertAlmostEqual(features[3].geometry().constGet().x(), -7591989, -5)
self.assertAlmostEqual(features[4]['virtual'], -65.32, 2)
self.assertAlmostEqual(features[4].geometry().constGet().x(), -7271389, -5)
class TestQgsVectorLayerSourceAddedFeaturesInBuffer(unittest.TestCase, FeatureSourceTestCase):
@classmethod
def getSource(cls):
vl = QgsVectorLayer(
'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk',
'test', 'memory')
assert (vl.isValid())
f1 = QgsFeature()
f1.setAttributes([5, -200, NULL, 'NuLl', '5'])
f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
f2 = QgsFeature()
f2.setAttributes([3, 300, 'Pear', 'PEaR', '3'])
f3 = QgsFeature()
f3.setAttributes([1, 100, 'Orange', 'oranGe', '1'])
f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)'))
f4 = QgsFeature()
f4.setAttributes([2, 200, 'Apple', 'Apple', '2'])
f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)'))
f5 = QgsFeature()
f5.setAttributes([4, 400, 'Honey', 'Honey', '4'])
f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)'))
# create a layer with features only in the added features buffer - not the provider
vl.startEditing()
vl.addFeatures([f1, f2, f3, f4, f5])
return vl
@classmethod
def setUpClass(cls):
"""Run before all tests"""
# Create test layer for FeatureSourceTestCase
cls.source = cls.getSource()
def testGetFeaturesSubsetAttributes2(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testGetFeaturesNoGeometry(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testOrderBy(self):
""" Skip order by tests - edited features are not sorted in iterators.
(Maybe they should be??)
"""
pass
def testMinimumValue(self):
""" Skip min values test - due to inconsistencies in how null values are treated by providers.
They are included here, but providers don't include them.... which is right?
"""
pass
class TestQgsVectorLayerSourceChangedGeometriesInBuffer(unittest.TestCase, FeatureSourceTestCase):
@classmethod
def getSource(cls):
vl = QgsVectorLayer(
'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk',
'test', 'memory')
assert (vl.isValid())
f1 = QgsFeature()
f1.setAttributes([5, -200, NULL, 'NuLl', '5'])
f2 = QgsFeature()
f2.setAttributes([3, 300, 'Pear', 'PEaR', '3'])
f2.setGeometry(QgsGeometry.fromWkt('Point (-70.5 65.2)'))
f3 = QgsFeature()
f3.setAttributes([1, 100, 'Orange', 'oranGe', '1'])
f4 = QgsFeature()
f4.setAttributes([2, 200, 'Apple', 'Apple', '2'])
f5 = QgsFeature()
f5.setAttributes([4, 400, 'Honey', 'Honey', '4'])
vl.dataProvider().addFeatures([f1, f2, f3, f4, f5])
ids = {f['pk']: f.id() for f in vl.getFeatures()}
# modify geometries in buffer
vl.startEditing()
vl.changeGeometry(ids[5], QgsGeometry.fromWkt('Point (-71.123 78.23)'))
vl.changeGeometry(ids[3], QgsGeometry())
vl.changeGeometry(ids[1], QgsGeometry.fromWkt('Point (-70.332 66.33)'))
vl.changeGeometry(ids[2], QgsGeometry.fromWkt('Point (-68.2 70.8)'))
vl.changeGeometry(ids[4], QgsGeometry.fromWkt('Point (-65.32 78.3)'))
return vl
@classmethod
def setUpClass(cls):
"""Run before all tests"""
# Create test layer for FeatureSourceTestCase
cls.source = cls.getSource()
def testGetFeaturesSubsetAttributes2(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testGetFeaturesNoGeometry(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testOrderBy(self):
""" Skip order by tests - edited features are not sorted in iterators.
(Maybe they should be??)
"""
pass
class TestQgsVectorLayerSourceChangedAttributesInBuffer(unittest.TestCase, FeatureSourceTestCase):
@classmethod
def getSource(cls):
vl = QgsVectorLayer(
'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk',
'test', 'memory')
assert (vl.isValid())
f1 = QgsFeature()
f1.setAttributes([5, 200, 'a', 'b', 'c'])
f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
f2 = QgsFeature()
f2.setAttributes([3, -200, 'd', 'e', 'f'])
f3 = QgsFeature()
f3.setAttributes([1, -100, 'g', 'h', 'i'])
f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)'))
f4 = QgsFeature()
f4.setAttributes([2, -200, 'j', 'k', 'l'])
f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)'))
f5 = QgsFeature()
f5.setAttributes([4, 400, 'm', 'n', 'o'])
f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)'))
vl.dataProvider().addFeatures([f1, f2, f3, f4, f5])
ids = {f['pk']: f.id() for f in vl.getFeatures()}
# modify geometries in buffer
vl.startEditing()
vl.changeAttributeValue(ids[5], 1, -200)
vl.changeAttributeValue(ids[5], 2, NULL)
vl.changeAttributeValue(ids[5], 3, 'NuLl')
vl.changeAttributeValue(ids[5], 4, '5')
vl.changeAttributeValue(ids[3], 1, 300)
vl.changeAttributeValue(ids[3], 2, 'Pear')
vl.changeAttributeValue(ids[3], 3, 'PEaR')
vl.changeAttributeValue(ids[3], 4, '3')
vl.changeAttributeValue(ids[1], 1, 100)
vl.changeAttributeValue(ids[1], 2, 'Orange')
vl.changeAttributeValue(ids[1], 3, 'oranGe')
vl.changeAttributeValue(ids[1], 4, '1')
vl.changeAttributeValue(ids[2], 1, 200)
vl.changeAttributeValue(ids[2], 2, 'Apple')
vl.changeAttributeValue(ids[2], 3, 'Apple')
vl.changeAttributeValue(ids[2], 4, '2')
vl.changeAttributeValue(ids[4], 1, 400)
vl.changeAttributeValue(ids[4], 2, 'Honey')
vl.changeAttributeValue(ids[4], 3, 'Honey')
vl.changeAttributeValue(ids[4], 4, '4')
return vl
@classmethod
def setUpClass(cls):
"""Run before all tests"""
# Create test layer for FeatureSourceTestCase
cls.source = cls.getSource()
def testGetFeaturesSubsetAttributes2(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testGetFeaturesNoGeometry(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testOrderBy(self):
""" Skip order by tests - edited features are not sorted in iterators.
(Maybe they should be??)
"""
pass
def testUniqueValues(self):
""" Skip unique values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMinimumValue(self):
""" Skip min values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMaximumValue(self):
""" Skip max values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
class TestQgsVectorLayerSourceDeletedFeaturesInBuffer(unittest.TestCase, FeatureSourceTestCase):
@classmethod
def getSource(cls):
vl = QgsVectorLayer(
'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk',
'test', 'memory')
assert (vl.isValid())
# add a bunch of similar features to the provider
b1 = QgsFeature()
b1.setAttributes([5, -300, 'Apple', 'PEaR', '1'])
b1.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)'))
b2 = QgsFeature()
b2.setAttributes([3, 100, 'Orange', 'NuLl', '2'])
b2.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
b3 = QgsFeature()
b3.setAttributes([1, -200, 'Honey', 'oranGe', '5'])
b4 = QgsFeature()
b4.setAttributes([2, 400, 'Pear', 'Honey', '3'])
b4.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)'))
b5 = QgsFeature()
b5.setAttributes([4, 200, NULL, 'oranGe', '3'])
b5.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)'))
vl.dataProvider().addFeatures([b1, b2, b3, b4, b5])
bad_ids = [f['pk'] for f in vl.getFeatures()]
# here's our good features
f1 = QgsFeature()
f1.setAttributes([5, -200, NULL, 'NuLl', '5'])
f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)'))
f2 = QgsFeature()
f2.setAttributes([3, 300, 'Pear', 'PEaR', '3'])
f3 = QgsFeature()
f3.setAttributes([1, 100, 'Orange', 'oranGe', '1'])
f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)'))
f4 = QgsFeature()
f4.setAttributes([2, 200, 'Apple', 'Apple', '2'])
f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)'))
f5 = QgsFeature()
f5.setAttributes([4, 400, 'Honey', 'Honey', '4'])
f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)'))
vl.dataProvider().addFeatures([f1, f2, f3, f4, f5])
# delete the bad features, but don't commit
vl.startEditing()
vl.deleteFeatures(bad_ids)
return vl
@classmethod
def setUpClass(cls):
"""Run before all tests"""
# Create test layer for FeatureSourceTestCase
cls.source = cls.getSource()
def testGetFeaturesSubsetAttributes2(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testGetFeaturesNoGeometry(self):
""" Override and skip this QgsFeatureSource test. We are using a memory provider, and it's actually more efficient for the memory provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
def testOrderBy(self):
""" Skip order by tests - edited features are not sorted in iterators.
(Maybe they should be??)
"""
pass
def testUniqueValues(self):
""" Skip unique values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMinimumValue(self):
""" Skip min values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
def testMaximumValue(self):
""" Skip max values test - as noted in the docs this is unreliable when features are in the buffer
"""
pass
# TODO:
# - fetch rect: feat with changed geometry: 1. in rect, 2. out of rect
# - more join tests
# - import
if __name__ == '__main__':
unittest.main()