Skip to content

Commit

Permalink
Merge pull request #4680 from nyalldawson/unique_values
Browse files Browse the repository at this point in the history
Move uniqueValues to QgsFeatureSource
  • Loading branch information
nyalldawson committed Jun 5, 2017
2 parents 27850fb + 3388857 commit 09e0365
Show file tree
Hide file tree
Showing 30 changed files with 195 additions and 111 deletions.
2 changes: 2 additions & 0 deletions doc/api_break.dox
Expand Up @@ -2275,6 +2275,7 @@ clause fragment which must be evaluated by the provider in order to calculate th
QGIS 3.0 defaultValue() only returns literal, constant defaultValues. A new method defaultValueClause
has been added which returns the SQL clause fragments which must be evaluated by the provider itself.
- isSaveAndLoadStyleToDBSupported() was renamed to isSaveAndLoadStyleToDatabaseSupported()
- The c++ signature for uniqueValues() has changed (the PyQGIS method remains unchanged)


QgsVectorJoinInfo {#qgis_api_break_3_0_QgsVectorJoinInfo}
Expand Down Expand Up @@ -2335,6 +2336,7 @@ displayExpression instead. For the map tip use mapTipTemplate() instead.
- addFeatures() no longer accepts a makeSelected boolean, and will not automatically select newly added features. If desired, features must be manually selected by calling selectByIds() after addFeatures()
- annotationForm() and setAnnotationForm() have been removed. Form path is stored in individual QgsFormAnnotation objects.
- setLayerTransparency, layerTransparency, and layerTransparencyChanged were removed. Use opacity, setOpacity and opacityChanged instead.
- The c++ signature for uniqueValues() has changed (the PyQGIS method remains unchanged)


QgsVectorLayerEditBuffer {#qgis_api_break_3_0_QgsVectorLayerEditBuffer}
Expand Down
9 changes: 9 additions & 0 deletions python/core/qgsfeaturesource.sip
Expand Up @@ -69,6 +69,15 @@ class QgsFeatureSource
:rtype: long
%End

virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;
%Docstring
Returns the set of unique values contained within the specified ``fieldIndex`` from this source.
If specified, the ``limit`` option can be used to limit the number of returned values.
The base class implementation uses a non-optimised approach of looping through
all features in the source.
:rtype: set of QVariant
%End

};


Expand Down
10 changes: 0 additions & 10 deletions python/core/qgsvectordataprovider.sip
Expand Up @@ -156,16 +156,6 @@ Bitmask of all provider's editing capabilities
:rtype: QVariant
%End

virtual void uniqueValues( int index, QList<QVariant> &uniqueValues /Out/, int limit = -1 ) const;
%Docstring
Return unique values of an attribute
\param index the index of the attribute
\param uniqueValues values reference to the list to fill
\param limit maxmum number of the values to return

Default implementation simply iterates the features
%End

virtual QStringList uniqueStringsMatching( int index, const QString &substring, int limit = -1,
QgsFeedback *feedback = 0 ) const;
%Docstring
Expand Down
7 changes: 4 additions & 3 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1507,18 +1507,19 @@ Assembles mUpdatedFields considering provider fields, joined fields and added fi
:rtype: QgsEditorWidgetSetup
%End

void uniqueValues( int index, QList<QVariant> &uniqueValues /Out/, int limit = -1 ) const;
virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;

%Docstring
Calculates a list of unique values contained within an attribute in the layer. Note that
in some circumstances when unsaved changes are present for the layer then the returned list
may contain outdated values (for instance when the attribute value in a saved feature has
been changed inside the edit buffer then the previous saved value will be included in the
returned list).
\param index column index for attribute
\param uniqueValues out: result list
\param fieldIndex column index for attribute
\param limit maximum number of values to return (or -1 if unlimited)
.. seealso:: minimumValue()
.. seealso:: maximumValue()
:rtype: set of QVariant
%End

QStringList uniqueStringsMatching( int index, const QString &substring, int limit = -1,
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -158,6 +158,7 @@ SET(QGIS_CORE_SRCS
qgsfeatureiterator.cpp
qgsfeaturerequest.cpp
qgsfeaturesink.cpp
qgsfeaturesource.cpp
qgsfeaturestore.cpp
qgsfield.cpp
qgsfieldconstraints.cpp
Expand Down
3 changes: 1 addition & 2 deletions src/core/dxf/qgsdxfexport.cpp
Expand Up @@ -787,8 +787,7 @@ void QgsDxfExport::writeTables()
}
else
{
QList<QVariant> values;
vl->uniqueValues( attrIdx, values );
QSet<QVariant> values = vl->uniqueValues( attrIdx );
Q_FOREACH ( const QVariant &v, values )
{
layerNames << dxfLayerName( v.toString() );
Expand Down
5 changes: 2 additions & 3 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -274,9 +274,8 @@ QList<QVariant> QgsProcessingUtils::uniqueValues( QgsVectorLayer *layer, int fie
if ( !useSelection )
{
// not using selection, so use provider optimised version
QList<QVariant> values;
layer->uniqueValues( fieldIndex, values );
return values;
QSet<QVariant> values = layer->uniqueValues( fieldIndex );
return values.toList();
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgis.h
Expand Up @@ -173,7 +173,7 @@ template<class Object> inline QgsSignalBlocker<Object> whileBlocking( Object *ob
}

//! Hash for QVariant
uint qHash( const QVariant &variant );
CORE_EXPORT uint qHash( const QVariant &variant );

//! Returns a string representation of a double
//! \param a double value
Expand Down
42 changes: 42 additions & 0 deletions src/core/qgsfeaturesource.cpp
@@ -0,0 +1,42 @@
/***************************************************************************
qgsfeaturesource.cpp
-------------------
begin : May 2017
copyright : (C) 2017 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 "qgsfeaturesource.h"
#include "qgsfeaturerequest.h"
#include "qgsfeatureiterator.h"

QSet<QVariant> QgsFeatureSource::uniqueValues( int fieldIndex, int limit ) const
{
if ( fieldIndex < 0 || fieldIndex >= fields().count() )
return QSet<QVariant>();

QgsFeatureRequest req;
req.setFlags( QgsFeatureRequest::NoGeometry );
req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex );

QSet<QVariant> values;
QgsFeatureIterator it = getFeatures( req );
QgsFeature f;
while ( it.nextFeature( f ) )
{
values.insert( f.attribute( fieldIndex ) );
if ( limit > 0 && values.size() >= limit )
return values;
}
return values;
}

10 changes: 9 additions & 1 deletion src/core/qgsfeaturesource.h 100755 → 100644
Expand Up @@ -20,9 +20,9 @@

#include "qgis_core.h"
#include "qgis.h"
#include "qgsfeaturerequest.h"

class QgsFeatureIterator;
class QgsFeatureRequest;
class QgsCoordinateReferenceSystem;
class QgsFields;

Expand Down Expand Up @@ -79,6 +79,14 @@ class CORE_EXPORT QgsFeatureSource
*/
virtual long featureCount() const = 0;

/**
* Returns the set of unique values contained within the specified \a fieldIndex from this source.
* If specified, the \a limit option can be used to limit the number of returned values.
* The base class implementation uses a non-optimised approach of looping through
* all features in the source.
*/
virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;

};

Q_DECLARE_METATYPE( QgsFeatureSource * )
Expand Down
22 changes: 0 additions & 22 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -433,28 +433,6 @@ QVariant QgsVectorDataProvider::maximumValue( int index ) const
return mCacheMaxValues[index];
}

void QgsVectorDataProvider::uniqueValues( int index, QList<QVariant> &values, int limit ) const
{
QgsFeature f;
QgsAttributeList keys;
keys.append( index );
QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setSubsetOfAttributes( keys ).setFlags( QgsFeatureRequest::NoGeometry ) );

QSet<QString> set;
values.clear();

while ( fi.nextFeature( f ) )
{
if ( !set.contains( f.attribute( index ).toString() ) )
{
values.append( f.attribute( index ) );
set.insert( f.attribute( index ).toString() );
}

if ( limit >= 0 && values.size() >= limit )
break;
}
}

QStringList QgsVectorDataProvider::uniqueStringsMatching( int index, const QString &substring, int limit, QgsFeedback *feedback ) const
{
Expand Down
10 changes: 0 additions & 10 deletions src/core/qgsvectordataprovider.h
Expand Up @@ -186,16 +186,6 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
*/
virtual QVariant maximumValue( int index ) const;

/**
* Return unique values of an attribute
* \param index the index of the attribute
* \param uniqueValues values reference to the list to fill
* \param limit maxmum number of the values to return
*
* Default implementation simply iterates the features
*/
virtual void uniqueValues( int index, QList<QVariant> &uniqueValues SIP_OUT, int limit = -1 ) const;

/**
* Returns unique string values of an attribute which contain a specified subset string. Subset
* matching is done in a case-insensitive manner.
Expand Down
20 changes: 10 additions & 10 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -3010,23 +3010,23 @@ QString QgsVectorLayer::defaultValueExpression( int index ) const
return mFields.at( index ).defaultValueExpression();
}

void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int limit ) const
QSet<QVariant> QgsVectorLayer::uniqueValues( int index, int limit ) const
{
uniqueValues.clear();
QSet<QVariant> uniqueValues;
if ( !mDataProvider )
{
return;
return uniqueValues;
}

QgsFields::FieldOrigin origin = mFields.fieldOrigin( index );
switch ( origin )
{
case QgsFields::OriginUnknown:
return;
return uniqueValues;

case QgsFields::OriginProvider: //a provider field
{
mDataProvider->uniqueValues( index, uniqueValues, limit );
uniqueValues = mDataProvider->uniqueValues( index, limit );

if ( mEditBuffer )
{
Expand Down Expand Up @@ -3070,7 +3070,7 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
}
}

return;
return uniqueValues;
}

case QgsFields::OriginEdit:
Expand All @@ -3080,8 +3080,8 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
!mEditBuffer->mDeletedAttributeIds.contains( index ) &&
mEditBuffer->mChangedAttributeValues.isEmpty() )
{
mDataProvider->uniqueValues( index, uniqueValues, limit );
return;
uniqueValues = mDataProvider->uniqueValues( index, limit );
return uniqueValues;
}
FALLTHROUGH;
//we need to go through each feature
Expand All @@ -3108,12 +3108,12 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
}
}

uniqueValues = val.values();
return;
return val.values().toSet();
}
}

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

QStringList QgsVectorLayer::uniqueStringsMatching( int index, const QString &substring, int limit, QgsFeedback *feedback ) const
Expand Down
5 changes: 2 additions & 3 deletions src/core/qgsvectorlayer.h
Expand Up @@ -1423,13 +1423,12 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* may contain outdated values (for instance when the attribute value in a saved feature has
* been changed inside the edit buffer then the previous saved value will be included in the
* returned list).
* \param index column index for attribute
* \param uniqueValues out: result list
* \param fieldIndex column index for attribute
* \param limit maximum number of values to return (or -1 if unlimited)
* \see minimumValue()
* \see maximumValue()
*/
void uniqueValues( int index, QList<QVariant> &uniqueValues SIP_OUT, int limit = -1 ) const;
QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const override;

/**
* Returns unique string values of an attribute which contain a specified subset string. Subset
Expand Down
3 changes: 1 addition & 2 deletions src/gui/editorwidgets/qgsrelationreferencewidget.cpp
Expand Up @@ -466,13 +466,12 @@ void QgsRelationReferenceWidget::init()
{
Q_FOREACH ( const QString &fieldName, mFilterFields )
{
QVariantList uniqueValues;
int idx = mReferencedLayer->fields().lookupField( fieldName );
QComboBox *cb = new QComboBox();
cb->setProperty( "Field", fieldName );
cb->setProperty( "FieldAlias", mReferencedLayer->attributeDisplayName( idx ) );
mFilterComboBoxes << cb;
mReferencedLayer->uniqueValues( idx, uniqueValues );
QVariantList uniqueValues = mReferencedLayer->uniqueValues( idx ).toList();
cb->addItem( mReferencedLayer->attributeDisplayName( idx ) );
QVariant nullValue = QgsApplication::nullRepresentation();
cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->fields().at( idx ).type() ) );
Expand Down
4 changes: 1 addition & 3 deletions src/gui/editorwidgets/qgsuniquevaluewidgetwrapper.cpp
Expand Up @@ -61,9 +61,7 @@ void QgsUniqueValuesWidgetWrapper::initWidget( QWidget *editor )

QStringList sValues;

QList<QVariant> values;

layer()->uniqueValues( fieldIdx(), values );
QSet< QVariant> values = layer()->uniqueValues( fieldIdx() );

Q_FOREACH ( const QVariant &v, values )
{
Expand Down
3 changes: 1 addition & 2 deletions src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -336,9 +336,8 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int
if ( fieldIndex < 0 )
return;

QList<QVariant> values;
QStringList strValues;
mLayer->uniqueValues( fieldIndex, values, countLimit );
QSet<QVariant> values = mLayer->uniqueValues( fieldIndex, countLimit );
Q_FOREACH ( const QVariant &value, values )
{
QString strValue;
Expand Down
18 changes: 8 additions & 10 deletions src/gui/qgsquerybuilder.cpp
Expand Up @@ -117,29 +117,27 @@ void QgsQueryBuilder::fillValues( int idx, int limit )
mModelValues->clear();

// determine the field type
QList<QVariant> values;
mLayer->uniqueValues( idx, values, limit );
QSet<QVariant> values = mLayer->uniqueValues( idx, limit );

QgsSettings settings;
QString nullValue = QgsApplication::nullRepresentation();

QgsDebugMsg( QString( "nullValue: %1" ).arg( nullValue ) );

for ( int i = 0; i < values.size(); i++ )
Q_FOREACH ( const QVariant &var, values )
{
QString value;
if ( values[i].isNull() )
if ( var.isNull() )
value = nullValue;
else if ( values[i].type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
value = values[i].toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
else if ( var.type() == QVariant::Date && mLayer->providerType() == QLatin1String( "ogr" ) && mLayer->storageType() == QLatin1String( "ESRI Shapefile" ) )
value = var.toDate().toString( QStringLiteral( "yyyy/MM/dd" ) );
else
value = values[i].toString();
value = var.toString();

QStandardItem *myItem = new QStandardItem( value );
myItem->setEditable( false );
myItem->setData( values[i], Qt::UserRole + 1 );
myItem->setData( var, Qt::UserRole + 1 );
mModelValues->insertRow( mModelValues->rowCount(), myItem );
QgsDebugMsg( QString( "Value is null: %1\nvalue: %2" ).arg( values[i].isNull() ).arg( values[i].isNull() ? nullValue : values[i].toString() ) );
QgsDebugMsg( QString( "Value is null: %1\nvalue: %2" ).arg( var.isNull() ).arg( var.isNull() ? nullValue : var.toString() ) );
}
}

Expand Down
Expand Up @@ -649,7 +649,7 @@ void QgsCategorizedSymbolRendererWidget::addCategories()
}
else
{
mLayer->uniqueValues( idx, unique_vals );
unique_vals = mLayer->uniqueValues( idx ).toList();
}

// ask to abort if too many classes
Expand Down

0 comments on commit 09e0365

Please sign in to comment.