Skip to content

Commit

Permalink
Don't wastefully recalculate memory provider extent after every
Browse files Browse the repository at this point in the history
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 6a87889
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 27 deletions.
47 changes: 26 additions & 21 deletions src/core/providers/memory/qgsmemoryprovider.cpp
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 )
{
Expand All @@ -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;
}

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

updateExtent();
updateExtents();

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

updateExtent();
updateExtents();

return true;
}
Expand Down Expand Up @@ -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
Expand Down
8 changes: 2 additions & 6 deletions src/core/providers/memory/qgsmemoryprovider.h
Expand Up @@ -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;
Expand Down

0 comments on commit 6a87889

Please sign in to comment.