QGIS/src/core/qgsvectorlayerfeatureiterator.cpp
2014-02-24 00:43:20 +01:00

709 lines
21 KiB
C++

/***************************************************************************
qgsvectorlayerfeatureiterator.cpp
---------------------
begin : Dezember 2012
copyright : (C) 2012 by Martin Dobias
email : wonder dot sk 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 "qgsvectorlayerfeatureiterator.h"
#include "qgsmaplayerregistry.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayereditbuffer.h"
#include "qgsvectorlayerjoinbuffer.h"
#include "qgsgeometrysimplifier.h"
#include "qgssimplifymethod.h"
QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( QgsVectorLayer *layer )
{
mProviderFeatureSource = layer->dataProvider()->featureSource();
mFields = layer->pendingFields();
mJoinBuffer = new QgsVectorLayerJoinBuffer( *layer->mJoinBuffer );
mCanBeSimplified = layer->hasGeometryType() && layer->geometryType() != QGis::Point;
mHasEditBuffer = layer->editBuffer();
if ( mHasEditBuffer )
{
#if 0
// TODO[MD]: after merge
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
// only copy relevant parts
if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
mDeletedFeatureIds.insert( request.filterFid() );
if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
mChangedFeaturesRequest.setFilterFids( QgsFeatureIds() << request.filterFid() );
}
else
{
#endif
mAddedFeatures = QgsFeatureMap( layer->editBuffer()->addedFeatures() );
mChangedGeometries = QgsGeometryMap( layer->editBuffer()->changedGeometries() );
mDeletedFeatureIds = QgsFeatureIds( layer->editBuffer()->deletedFeatureIds() );
mChangedAttributeValues = QgsChangedAttributesMap( layer->editBuffer()->changedAttributeValues() );
mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
mDeletedAttributeIds = QgsAttributeList( layer->editBuffer()->deletedAttributeIds() );
#if 0
}
#endif
}
}
QgsVectorLayerFeatureSource::~QgsVectorLayerFeatureSource()
{
delete mJoinBuffer;
delete mProviderFeatureSource;
}
QgsFeatureIterator QgsVectorLayerFeatureSource::getFeatures( const QgsFeatureRequest& request )
{
// return feature iterator that does not own this source
return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, false, request ) );
}
QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
: QgsAbstractFeatureIteratorFromSource( source, ownSource, request )
, mEditGeometrySimplifier( 0 )
{
// prepare joins: may add more attributes to fetch (in order to allow join)
if ( mSource->mJoinBuffer->containsJoins() )
prepareJoins();
// by default provider's request is the same
mProviderRequest = mRequest;
if ( mProviderRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
{
// prepare list of attributes to match provider fields
QgsAttributeList providerSubset;
QgsAttributeList subset = mProviderRequest.subsetOfAttributes();
int nPendingFields = mSource->mFields.count();
for ( int i = 0; i < subset.count(); ++i )
{
int attrIndex = subset[i];
if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue;
if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
providerSubset << mSource->mFields.fieldOriginIndex( attrIndex );
}
mProviderRequest.setSubsetOfAttributes( providerSubset );
}
if ( mSource->mHasEditBuffer )
{
mChangedFeaturesRequest = mProviderRequest;
mChangedFeaturesRequest.setFilterFids( mSource->mChangedAttributeValues.keys().toSet() );
}
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
mFetchedFid = false;
}
else // no filter or filter by rect
{
if ( mSource->mHasEditBuffer )
{
mChangedFeaturesIterator = mSource->mProviderFeatureSource->getFeatures( mChangedFeaturesRequest );
}
else
{
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
}
rewindEditBuffer();
}
if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
{
mRequest.filterExpression()->prepare( mSource->mFields );
}
}
QgsVectorLayerFeatureIterator::~QgsVectorLayerFeatureIterator()
{
delete mEditGeometrySimplifier;
mEditGeometrySimplifier = NULL;
close();
}
bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f )
{
f.setValid( false );
if ( mClosed )
return false;
if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
{
if ( mFetchedFid )
return false;
bool res = nextFeatureFid( f );
mFetchedFid = true;
return res;
}
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
if ( fetchNextChangedGeomFeature( f ) )
return true;
// no more changed geometries
}
if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
{
if ( fetchNextChangedAttributeFeature( f ) )
return true;
// no more changed features
}
while ( fetchNextAddedFeature( f ) )
{
return true;
}
// no more added features
if ( mProviderIterator.isClosed() )
{
mChangedFeaturesIterator.close();
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
}
while ( mProviderIterator.nextFeature( f ) )
{
if ( mFetchConsidered.contains( f.id() ) )
continue;
// TODO[MD]: just one resize of attributes
f.setFields( &mSource->mFields );
// update attributes
updateChangedAttributes( f );
if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );
// update geometry
// TODO[MK]: FilterRect check after updating the geometry
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
updateFeatureGeometry( f );
return true;
}
// no more provider features
close();
return false;
}
bool QgsVectorLayerFeatureIterator::rewind()
{
if ( mClosed )
return false;
if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
{
mFetchedFid = false;
}
else
{
mProviderIterator.rewind();
rewindEditBuffer();
}
return true;
}
bool QgsVectorLayerFeatureIterator::close()
{
if ( mClosed )
return false;
mProviderIterator.close();
iteratorClosed();
mClosed = true;
return true;
}
bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature& f )
{
while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() )
{
QgsFeatureId fid = mFetchAddedFeaturesIt->id();
if ( mFetchConsidered.contains( fid ) )
// must have changed geometry outside rectangle
continue;
if ( !mRequest.acceptFeature( *mFetchAddedFeaturesIt ) )
// skip features which are not accepted by the filter
continue;
useAddedFeature( *mFetchAddedFeaturesIt, f );
return true;
}
mFetchAddedFeaturesIt = mSource->mAddedFeatures.constBegin();
return false; // no more added features
}
void QgsVectorLayerFeatureIterator::useAddedFeature( const QgsFeature& src, QgsFeature& f )
{
f.setFeatureId( src.id() );
f.setValid( true );
f.setFields( &mSource->mFields );
if ( src.geometry() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
{
f.setGeometry( *src.geometry() );
// simplify the edited geometry using its simplifier configured
if ( mEditGeometrySimplifier )
{
QgsGeometry* geometry = f.geometry();
QGis::GeometryType geometryType = geometry->type();
if ( geometryType == QGis::Line || geometryType == QGis::Polygon ) mEditGeometrySimplifier->simplifyGeometry( geometry );
}
}
// TODO[MD]: if subset set just some attributes
f.setAttributes( src.attributes() );
if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );
}
bool QgsVectorLayerFeatureIterator::fetchNextChangedGeomFeature( QgsFeature& f )
{
// check if changed geometries are in rectangle
for ( ; mFetchChangedGeomIt != mSource->mChangedGeometries.constEnd(); mFetchChangedGeomIt++ )
{
QgsFeatureId fid = mFetchChangedGeomIt.key();
if ( mFetchConsidered.contains( fid ) )
// skip deleted features
continue;
mFetchConsidered << fid;
if ( !mFetchChangedGeomIt->intersects( mRequest.filterRect() ) )
// skip changed geometries not in rectangle and don't check again
continue;
useChangedAttributeFeature( fid, *mFetchChangedGeomIt, f );
// return complete feature
mFetchChangedGeomIt++;
return true;
}
return false; // no more changed geometries
}
bool QgsVectorLayerFeatureIterator::fetchNextChangedAttributeFeature( QgsFeature& f )
{
while ( mChangedFeaturesIterator.nextFeature( f ) )
{
mFetchConsidered << f.id();
updateChangedAttributes( f );
if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );
if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
{
if ( mRequest.filterExpression()->evaluate( &f ).toBool() )
{
return true;
}
}
else
{
return true;
}
}
return false;
}
void QgsVectorLayerFeatureIterator::useChangedAttributeFeature( QgsFeatureId fid, const QgsGeometry& geom, QgsFeature& f )
{
f.setFeatureId( fid );
f.setValid( true );
f.setFields( &mSource->mFields );
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
{
f.setGeometry( geom );
// simplify the edited geometry using its simplifier configured
if ( mEditGeometrySimplifier )
{
QgsGeometry* geometry = f.geometry();
QGis::GeometryType geometryType = geometry->type();
if ( geometryType == QGis::Line || geometryType == QGis::Polygon ) mEditGeometrySimplifier->simplifyGeometry( geometry );
}
}
bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
if ( !subsetAttrs || ( subsetAttrs && mRequest.subsetOfAttributes().count() > 0 ) )
{
// retrieve attributes from provider
QgsFeature tmp;
//mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
QgsFeatureRequest request;
request.setFilterFid( fid ).setFlags( QgsFeatureRequest::NoGeometry );
if ( subsetAttrs )
{
request.setSubsetOfAttributes( mProviderRequest.subsetOfAttributes() );
}
QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( request );
if ( fi.nextFeature( tmp ) )
{
updateChangedAttributes( tmp );
f.setAttributes( tmp.attributes() );
}
}
if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );
}
void QgsVectorLayerFeatureIterator::rewindEditBuffer()
{
mFetchConsidered = mSource->mDeletedFeatureIds;
mFetchAddedFeaturesIt = mSource->mAddedFeatures.constEnd();
mFetchChangedGeomIt = mSource->mChangedGeometries.constBegin();
}
void QgsVectorLayerFeatureIterator::prepareJoins()
{
QgsAttributeList fetchAttributes = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
QgsAttributeList sourceJoinFields; // attributes that also need to be fetched from this layer in order to have joins working
mFetchJoinInfo.clear();
for ( QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin(); attIt != fetchAttributes.constEnd(); ++attIt )
{
if ( !mSource->mFields.exists( *attIt ) )
continue;
if ( mSource->mFields.fieldOrigin( *attIt ) != QgsFields::OriginJoin )
continue;
int sourceLayerIndex;
const QgsVectorJoinInfo* joinInfo = mSource->mJoinBuffer->joinForFieldIndex( *attIt, mSource->mFields, sourceLayerIndex );
Q_ASSERT( joinInfo );
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
Q_ASSERT( joinLayer );
if ( !mFetchJoinInfo.contains( joinLayer ) )
{
FetchJoinInfo info;
info.joinInfo = joinInfo;
info.joinLayer = joinLayer;
if ( joinInfo->targetFieldName.isEmpty() )
info.targetField = joinInfo->targetFieldIndex; //for compatibility with 1.x
else
info.targetField = mSource->mFields.indexFromName( joinInfo->targetFieldName );
if ( joinInfo->joinFieldName.isEmpty() )
info.joinField = joinInfo->joinFieldIndex; //for compatibility with 1.x
else
info.joinField = joinLayer->pendingFields().indexFromName( joinInfo->joinFieldName );
info.indexOffset = *attIt - sourceLayerIndex;
if ( info.joinField < sourceLayerIndex )
info.indexOffset++;
// for joined fields, we always need to request the targetField from the provider too
if ( !fetchAttributes.contains( info.targetField ) )
sourceJoinFields << info.targetField;
mFetchJoinInfo.insert( joinLayer, info );
}
// store field source index - we'll need it when fetching from provider
mFetchJoinInfo[ joinLayer ].attributes.push_back( sourceLayerIndex );
}
// add sourceJoinFields if we're using a subset
if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() + sourceJoinFields );
}
void QgsVectorLayerFeatureIterator::addJoinedAttributes( QgsFeature &f )
{
// make sure we have space for newly added attributes
f.attributes().resize( mSource->mFields.count() ); // f.attributes().count() + mJoinedAttributesCount );
QMap<QgsVectorLayer*, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
{
const FetchJoinInfo& info = joinIt.value();
Q_ASSERT( joinIt.key() );
QVariant targetFieldValue = f.attribute( info.targetField );
if ( !targetFieldValue.isValid() )
continue;
const QHash< QString, QgsAttributes>& memoryCache = info.joinInfo->cachedAttributes;
if ( memoryCache.isEmpty() )
info.addJoinedAttributesDirect( f, targetFieldValue );
else
info.addJoinedAttributesCached( f, targetFieldValue );
}
}
bool QgsVectorLayerFeatureIterator::prepareSimplification( const QgsSimplifyMethod& simplifyMethod )
{
delete mEditGeometrySimplifier;
mEditGeometrySimplifier = NULL;
// setup simplification for edited geometries to fetch
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && simplifyMethod.methodType() != QgsSimplifyMethod::NoSimplification && mSource->mCanBeSimplified )
{
mEditGeometrySimplifier = QgsSimplifyMethod::createGeometrySimplifier( simplifyMethod );
return mEditGeometrySimplifier != NULL;
}
return false;
}
bool QgsVectorLayerFeatureIterator::providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const
{
#if 0
// TODO[MD]: after merge
QgsVectorDataProvider* provider = L->dataProvider();
if ( provider && methodType != QgsSimplifyMethod::NoSimplification )
{
int capabilities = provider->capabilities();
if ( methodType == QgsSimplifyMethod::OptimizeForRendering )
{
return ( capabilities & QgsVectorDataProvider::SimplifyGeometries );
}
else if ( methodType == QgsSimplifyMethod::PreserveTopology )
{
return ( capabilities & QgsVectorDataProvider::SimplifyGeometriesWithTopologicalValidation );
}
}
#endif
return false;
}
void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesCached( QgsFeature& f, const QVariant& joinValue ) const
{
const QHash<QString, QgsAttributes>& memoryCache = joinInfo->cachedAttributes;
QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
if ( it == memoryCache.constEnd() )
return; // joined value not found -> leaving the attributes empty (null)
int index = indexOffset;
const QgsAttributes& featureAttributes = it.value();
for ( int i = 0; i < featureAttributes.count(); ++i )
{
// skip the join field to avoid double field names (fields often have the same name)
if ( i == joinField )
continue;
f.setAttribute( index++, featureAttributes[i] );
}
}
void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesDirect( QgsFeature& f, const QVariant& joinValue ) const
{
// no memory cache, query the joined values by setting substring
QString subsetString = joinLayer->dataProvider()->subsetString(); // provider might already have a subset string
QString bkSubsetString = subsetString;
if ( !subsetString.isEmpty() )
{
subsetString.prepend( "(" ).append( ") AND " );
}
QString joinFieldName;
if ( joinInfo->joinFieldName.isEmpty() && joinInfo->joinFieldIndex >= 0 && joinInfo->joinFieldIndex < joinLayer->pendingFields().count() )
joinFieldName = joinLayer->pendingFields().field( joinInfo->joinFieldIndex ).name(); // for compatibility with 1.x
else
joinFieldName = joinInfo->joinFieldName;
subsetString.append( QString( "\"%1\"" ).arg( joinFieldName ) );
if ( joinValue.isNull() )
{
subsetString += " IS NULL";
}
else
{
QString v = joinValue.toString();
switch ( joinValue.type() )
{
case QVariant::Int:
case QVariant::LongLong:
case QVariant::Double:
break;
default:
case QVariant::String:
v.replace( "'", "''" );
v.prepend( "'" ).append( "'" );
break;
}
subsetString += "=" + v;
}
joinLayer->dataProvider()->setSubsetString( subsetString, false );
// select (no geometry)
QgsFeatureRequest request;
request.setFlags( QgsFeatureRequest::NoGeometry );
request.setSubsetOfAttributes( attributes );
QgsFeatureIterator fi = joinLayer->getFeatures( request );
// get first feature
QgsFeature fet;
if ( fi.nextFeature( fet ) )
{
int index = indexOffset;
const QgsAttributes& attr = fet.attributes();
for ( int i = 0; i < attr.count(); ++i )
{
if ( i == joinField )
continue;
f.setAttribute( index++, attr[i] );
}
}
else
{
// no suitable join feature found, keeping empty (null) attributes
}
joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
}
bool QgsVectorLayerFeatureIterator::nextFeatureFid( QgsFeature& f )
{
QgsFeatureId featureId = mRequest.filterFid();
// deleted already?
if ( mSource->mDeletedFeatureIds.contains( featureId ) )
return false;
// has changed geometry?
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mSource->mChangedGeometries.contains( featureId ) )
{
useChangedAttributeFeature( featureId, mSource->mChangedGeometries[featureId], f );
return true;
}
// added features
for ( QgsFeatureMap::ConstIterator iter = mSource->mAddedFeatures.constBegin(); iter != mSource->mAddedFeatures.constEnd(); ++iter )
{
if ( iter->id() == featureId )
{
useAddedFeature( *iter, f );
return true;
}
}
// regular features
QgsFeatureIterator fi = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
if ( fi.nextFeature( f ) )
{
updateChangedAttributes( f );
if ( !mFetchJoinInfo.isEmpty() )
addJoinedAttributes( f );
return true;
}
return false;
}
void QgsVectorLayerFeatureIterator::updateChangedAttributes( QgsFeature &f )
{
QgsAttributes& attrs = f.attributes();
// remove all attributes that will disappear - from higher indices to lower
for ( int idx = mSource->mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
{
attrs.remove( mSource->mDeletedAttributeIds[idx] );
}
// adjust size to accommodate added attributes
attrs.resize( attrs.count() + mSource->mAddedAttributes.count() );
// update changed attributes
if ( mSource->mChangedAttributeValues.contains( f.id() ) )
{
const QgsAttributeMap &map = mSource->mChangedAttributeValues[f.id()];
for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
attrs[it.key()] = it.value();
}
}
void QgsVectorLayerFeatureIterator::updateFeatureGeometry( QgsFeature &f )
{
if ( mSource->mChangedGeometries.contains( f.id() ) )
f.setGeometry( mSource->mChangedGeometries[f.id()] );
}