Skip to content
Permalink
Browse files

Don't wastefully recalculate memory provider extent after every

feature addition

Previously, the memory provider would automatically recalculate
the extent of the layer after new features are added by
looping through the entire set of existing features and calculating
the bounding boxes. This is very wasteful, as many code paths
add features one-by-one, so with every new feature added to
the provider every existing feature is iterated over. This caused
memory layers to slow to a crawl after many features are added.

This commit improves the logic so that IF an existing layer
extent is known, then it's updated on the fly as each individual
feauture is added. Instead of looping through all features, we
just expand the existing known extent with the added features
bounds. If the extent isn't known, we just invalidate it
when adding/deleting/modifying features, and defer the actual
extent calculation until it's next requested.

Makes memory layers many thousands of magnitudes of orders faster
when adding lots of features (e.g. when memory providers
are used as temporary outputs in processing)
  • Loading branch information
nyalldawson authored and m-kuhn committed Jun 11, 2017
1 parent be2ec2f commit 6a87889efcc6c57d0b47335b5947cef54bd4ec6d
Showing with 28 additions and 27 deletions.
  1. +26 −21 src/core/providers/memory/qgsmemoryprovider.cpp
  2. +2 −6 src/core/providers/memory/qgsmemoryprovider.h
@@ -272,6 +272,16 @@ QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &requ

QgsRectangle QgsMemoryProvider::extent() const
{
if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
{
mExtent.setMinimal();
Q_FOREACH ( const QgsFeature &feat, mFeatures )
{
if ( feat.hasGeometry() )
mExtent.unionRect( feat.geometry().boundingBox() );
}
}

return mExtent;
}

@@ -315,6 +325,9 @@ QgsCoordinateReferenceSystem QgsMemoryProvider::crs() const

bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist )
{
// whether or not to update the layer extent on the fly as we add features
bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();

// TODO: sanity checks of fields and geometries
for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end(); ++it )
{
@@ -323,15 +336,19 @@ bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist )

mFeatures.insert( mNextFeatureId, *it );

// update spatial index
if ( mSpatialIndex )
mSpatialIndex->insertFeature( *it );
if ( it->hasGeometry() )
{
if ( updateExtent )
mExtent.combineExtentWith( it->geometry().boundingBox() );

// update spatial index
if ( mSpatialIndex )
mSpatialIndex->insertFeature( *it );
}

mNextFeatureId++;
}

updateExtent();

return true;
}

@@ -352,7 +369,7 @@ bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
mFeatures.erase( fit );
}

updateExtent();
updateExtents();

return true;
}
@@ -471,7 +488,7 @@ bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map
mSpatialIndex->insertFeature( *fit );
}

updateExtent();
updateExtents();

return true;
}
@@ -522,21 +539,9 @@ QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
}


void QgsMemoryProvider::updateExtent()
void QgsMemoryProvider::updateExtents()
{
if ( mFeatures.isEmpty() )
{
mExtent = QgsRectangle();
}
else
{
mExtent.setMinimal();
Q_FOREACH ( const QgsFeature &feat, mFeatures )
{
if ( feat.hasGeometry() )
mExtent.unionRect( feat.geometry().boundingBox() );
}
}
mExtent.setMinimal();
}

QString QgsMemoryProvider::name() const
@@ -68,22 +68,18 @@ class QgsMemoryProvider : public QgsVectorDataProvider
QString name() const override;
QString description() const override;
virtual QgsRectangle extent() const override;
void updateExtents() override;
bool isValid() const override;
virtual QgsCoordinateReferenceSystem crs() const override;

protected:

// called when added / removed features or geometries has been changed
void updateExtent();

private:
// Coordinate reference system
QgsCoordinateReferenceSystem mCrs;

// fields
QgsFields mFields;
QgsWkbTypes::Type mWkbType;
QgsRectangle mExtent;
mutable QgsRectangle mExtent;

// features
QgsFeatureMap mFeatures;

0 comments on commit 6a87889

Please sign in to comment.
You can’t perform that action at this time.