mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
2911 lines
111 KiB
Python
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()
|