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)

(cherry-picked from 6a87889)
  • Loading branch information
nyalldawson committed Jun 12, 2017
1 parent e91e247 commit ebfa9f1
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 26 deletions.
47 changes: 26 additions & 21 deletions src/providers/memory/qgsmemoryprovider.cpp
Expand Up @@ -285,6 +285,16 @@ QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest& requ

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

return mExtent;
}

Expand Down Expand Up @@ -328,6 +338,9 @@ QgsCoordinateReferenceSystem QgsMemoryProvider::crs()

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 @@ -336,15 +349,19 @@ bool QgsMemoryProvider::addFeatures( QgsFeatureList & flist )

mFeatures.insert( mNextFeatureId, *it );

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

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

mNextFeatureId++;
}

updateExtent();

return true;
}

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

updateExtent();
updateExtents();

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

updateExtent();
updateExtents();

return true;
}
Expand Down Expand Up @@ -533,21 +550,9 @@ int 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.constGeometry() )
mExtent.unionRect( feat.constGeometry()->boundingBox() );
}
}
mExtent.setMinimal();
}


Expand Down
7 changes: 2 additions & 5 deletions src/providers/memory/qgsmemoryprovider.h
Expand Up @@ -158,10 +158,7 @@ class QgsMemoryProvider : public QgsVectorDataProvider

virtual QgsCoordinateReferenceSystem crs() override;

protected:

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

private:
// Coordinate reference system
Expand All @@ -170,7 +167,7 @@ class QgsMemoryProvider : public QgsVectorDataProvider
// fields
QgsFields mFields;
QGis::WkbType mWkbType;
QgsRectangle mExtent;
mutable QgsRectangle mExtent;

// features
QgsFeatureMap mFeatures;
Expand Down

0 comments on commit ebfa9f1

Please sign in to comment.