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)

(cherry-picked from 6a87889)
  • Loading branch information
nyalldawson committed Jun 12, 2017
1 parent e91e247 commit ebfa9f1479fba953aaa810b8b971d88055b14d0f
Showing with 28 additions and 26 deletions.
  1. +26 −21 src/providers/memory/qgsmemoryprovider.cpp
  2. +2 −5 src/providers/memory/qgsmemoryprovider.h
@@ -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;
}

@@ -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 )
{
@@ -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;
}

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

updateExtent();
updateExtents();

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

updateExtent();
updateExtents();

return true;
}
@@ -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();
}


@@ -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
@@ -170,7 +167,7 @@ class QgsMemoryProvider : public QgsVectorDataProvider
// fields
QgsFields mFields;
QGis::WkbType mWkbType;
QgsRectangle mExtent;
mutable QgsRectangle mExtent;

// features
QgsFeatureMap mFeatures;

0 comments on commit ebfa9f1

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