Skip to content

Commit

Permalink
New class QgsVectorLayerUtils
Browse files Browse the repository at this point in the history
Contains static helper methods for working with vector layers.
Initially only contains a method to test whether a value
exists within a layer's attributes.
  • Loading branch information
nyalldawson committed Nov 2, 2016
1 parent 3e40f80 commit b7d0fd6
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 0 deletions.
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -148,6 +148,7 @@
%Include qgsvectorlayerimport.sip
%Include qgsvectorlayerjoinbuffer.sip
%Include qgsvectorlayerundocommand.sip
%Include qgsvectorlayerutils.sip
%Include qgsvectorsimplifymethod.sip

%Include qgscachedfeatureiterator.sip
Expand Down
22 changes: 22 additions & 0 deletions python/core/qgsvectorlayerutils.sip
@@ -0,0 +1,22 @@

/** \ingroup core
* \class QgsVectorLayerUtils
* \brief Contains utility methods for working with QgsVectorLayers.
*
* \note Added in version 3.0
*/
class QgsVectorLayerUtils
{
%TypeHeaderCode
#include <qgsvectorlayerutils.h>
%End
public:

/**
* Returns true if the specified value already exists within a field. This method can be used to test for uniqueness
* of values inside a layer's attributes. An optional list of ignored feature IDs can be provided, if so, any features
* with IDs within this list are ignored when testing for existance of the value.
*/
static bool valueExists( const QgsVectorLayer* layer, int fieldIndex, const QVariant& value, const QgsFeatureIds& ignoreIds = QgsFeatureIds() );

};
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -230,6 +230,7 @@ SET(QGIS_CORE_SRCS
qgsvectorlayerlabelprovider.cpp
qgsvectorlayerrenderer.cpp
qgsvectorlayerundocommand.cpp
qgsvectorlayerutils.cpp
qgsvectorsimplifymethod.cpp
qgsvirtuallayerdefinition.cpp
qgsvirtuallayerdefinitionutils.cpp
Expand Down Expand Up @@ -722,6 +723,7 @@ SET(QGIS_CORE_HDRS
qgsvectorlayerlabelprovider.h
qgsvectorlayerrenderer.h
qgsvectorlayerundocommand.h
qgsvectorlayerutils.h
qgsvectorsimplifymethod.h
qgsmapthemecollection.h
qgsxmlutils.h
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectorlayer.h
Expand Up @@ -403,6 +403,7 @@ struct CORE_EXPORT QgsVectorJoinInfo
* Provider to display vector data in a GRASS GIS layer.
*
* TODO QGIS3: Remove virtual from non-inherited methods (like isModified)
* @see QgsVectorLayerUtils()
*/


Expand Down
51 changes: 51 additions & 0 deletions src/core/qgsvectorlayerutils.cpp
@@ -0,0 +1,51 @@
/***************************************************************************
qgsvectorlayerutils.cpp
-----------------------
Date : October 2016
Copyright : (C) 2016 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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. *
* *
***************************************************************************/

#include "qgsvectorlayerutils.h"

bool QgsVectorLayerUtils::valueExists( const QgsVectorLayer* layer, int fieldIndex, const QVariant& value, const QgsFeatureIds& ignoreIds )
{
if ( !layer )
return false;

if ( fieldIndex < 0 || fieldIndex >= layer->fields().count() )
return false;

QString fieldName = layer->fields().at( fieldIndex ).name();

// build up an optimised feature request
QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );
request.setFlags( QgsFeatureRequest::NoGeometry );

// at most we need to check ignoreIds.size() + 1 - the feature not in ignoreIds is the one we're interested in
int limit = ignoreIds.size() + 1;
request.setLimit( limit );

request.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( fieldName ),
QgsExpression::quotedValue( value ) ) );

QgsFeature feat;
QgsFeatureIterator it = layer->getFeatures( request );
while ( it.nextFeature( feat ) )
{
if ( ignoreIds.contains( feat.id() ) )
continue;

return true;
}

return false;
}
41 changes: 41 additions & 0 deletions src/core/qgsvectorlayerutils.h
@@ -0,0 +1,41 @@
/***************************************************************************
qgsvectorlayerutils.h
---------------------
Date : October 2016
Copyright : (C) 2016 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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. *
* *
***************************************************************************/

#ifndef QGSVECTORLAYERUTILS_H
#define QGSVECTORLAYERUTILS_H

#include "qgsvectorlayer.h"

/** \ingroup core
* \class QgsVectorLayerUtils
* \brief Contains utility methods for working with QgsVectorLayers.
*
* \note Added in version 3.0
*/

class CORE_EXPORT QgsVectorLayerUtils
{
public:

/**
* Returns true if the specified value already exists within a field. This method can be used to test for uniqueness
* of values inside a layer's attributes. An optional list of ignored feature IDs can be provided, if so, any features
* with IDs within this list are ignored when testing for existance of the value.
*/
static bool valueExists( const QgsVectorLayer* layer, int fieldIndex, const QVariant& value, const QgsFeatureIds& ignoreIds = QgsFeatureIds() );

};

#endif // QGSVECTORLAYERUTILS_H
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -111,6 +111,7 @@ ADD_PYTHON_TEST(PyQgsVectorColorRamp test_qgsvectorcolorramp.py)
ADD_PYTHON_TEST(PyQgsVectorFileWriter test_qgsvectorfilewriter.py)
ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
ADD_PYTHON_TEST(PyQgsVectorLayerEditBuffer test_qgsvectorlayereditbuffer.py)
ADD_PYTHON_TEST(PyQgsVectorLayerUtils test_qgsvectorlayerutils.py)
ADD_PYTHON_TEST(PyQgsZonalStatistics test_qgszonalstatistics.py)
ADD_PYTHON_TEST(PyQgsMapLayerRegistry test_qgsmaplayerregistry.py)
ADD_PYTHON_TEST(PyQgsVirtualLayerProvider test_provider_virtual.py)
Expand Down
88 changes: 88 additions & 0 deletions tests/src/python/test_qgsvectorlayerutils.py
@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsVectorLayerUtils.
.. 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__ = 'Nyall Dawson'
__date__ = '25/10/2016'
__copyright__ = 'Copyright 2016, 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

from qgis.core import (QgsVectorLayer,
QgsVectorLayerUtils,
QgsField,
QgsFields,
QgsFeature
)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
start_app()


def createLayerWithOnePoint():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
pr = layer.dataProvider()
f = QgsFeature()
f.setAttributes(["test", 123])
assert pr.addFeatures([f])
assert layer.pendingFeatureCount() == 1
return layer


class TestQgsVectorLayerUtils(unittest.TestCase):

def test_value_exists(self):
layer = createLayerWithOnePoint()
# add some more features
f1 = QgsFeature(2)
f1.setAttributes(["test1", 124])
f2 = QgsFeature(3)
f2.setAttributes(["test2", 125])
f3 = QgsFeature(4)
f3.setAttributes(["test3", 126])
f4 = QgsFeature(5)
f4.setAttributes(["test4", 127])
layer.dataProvider().addFeatures([f1, f2, f3, f4])

self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test'))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1'))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test4'))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'not present!'))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 123))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 124))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 127))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 99))

# no layer
self.assertFalse(QgsVectorLayerUtils.valueExists(None, 1, 123))
# bad field indexes
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, -1, 'test'))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 100, 'test'))

# with ignore list
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [3, 4, 5]))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [999999]))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [2]))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [99999, 2]))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [3, 4, 5, 2]))

self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 125, [2, 4, 5]))
self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 125, [999999]))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [3]))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [99999, 3]))
self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [2, 4, 5, 3]))


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

0 comments on commit b7d0fd6

Please sign in to comment.