Skip to content

Commit 9d316b0

Browse files
committedOct 18, 2016
[OGR provider] Update layer extent for GPKG layers
When moving or deleting a geometry that previously touched the layer extent, the layer extent was never shrinked. This fix requires GDAL 2.1.2 or above as well. Fixes #15273
1 parent e22b195 commit 9d316b0

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed
 

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
287287
, mFirstFieldIsFid( false )
288288
, ogrDataSource( nullptr )
289289
, mExtent( nullptr )
290+
, mForceRecomputeExtent( false )
290291
, ogrLayer( nullptr )
291292
, ogrOrigLayer( nullptr )
292293
, mLayerIndex( 0 )
@@ -483,7 +484,7 @@ bool QgsOgrProvider::setSubsetString( const QString& theSQL, bool updateFeatureC
483484
loadFields();
484485
QgsDebugMsg( "Done checking validity" );
485486

486-
updateExtents();
487+
invalidateCachedExtent( false );
487488

488489
emit dataChanged();
489490

@@ -978,6 +979,17 @@ QgsRectangle QgsOgrProvider::extent()
978979
// get the extent_ (envelope) of the layer
979980
QgsDebugMsg( "Starting get extent" );
980981

982+
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,2)
983+
if ( mForceRecomputeExtent && mValid && ogrDriverName == "GPKG" && ogrDataSource && ogrOrigLayer )
984+
{
985+
QByteArray layerName = OGR_FD_GetName( OGR_L_GetLayerDefn( ogrOrigLayer ) );
986+
// works with unquoted layerName
987+
QByteArray sql = QByteArray( "RECOMPUTE EXTENT ON " ) + layerName;
988+
QgsDebugMsg( QString( "SQL: %1" ).arg( FROM8( sql ) ) );
989+
OGR_DS_ExecuteSQL( ogrDataSource, sql.constData(), nullptr, nullptr );
990+
}
991+
#endif
992+
981993
// TODO: This can be expensive, do we really need it!
982994
if ( ogrLayer == ogrOrigLayer )
983995
{
@@ -1021,6 +1033,12 @@ QgsRectangle QgsOgrProvider::extent()
10211033

10221034
void QgsOgrProvider::updateExtents()
10231035
{
1036+
invalidateCachedExtent( true );
1037+
}
1038+
1039+
void QgsOgrProvider::invalidateCachedExtent( bool bForceRecomputeExtent )
1040+
{
1041+
mForceRecomputeExtent = bForceRecomputeExtent;
10241042
delete mExtent;
10251043
mExtent = nullptr;
10261044
}
@@ -1665,6 +1683,8 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
16651683
}
16661684
mShapefileMayBeCorrupted = true;
16671685

1686+
invalidateCachedExtent( true );
1687+
16681688
OGR_F_Destroy( theOGRFeature );
16691689
}
16701690
QgsOgrConnPool::instance()->invalidateConnections( dataSourceUri() );
@@ -1734,7 +1754,7 @@ bool QgsOgrProvider::deleteFeatures( const QgsFeatureIds & id )
17341754

17351755
clearMinMaxCache();
17361756

1737-
updateExtents();
1757+
invalidateCachedExtent( true );
17381758

17391759
return returnvalue;
17401760
}
@@ -3442,7 +3462,7 @@ void QgsOgrProvider::close()
34423462
mValid = false;
34433463
setProperty( "_debug_open_mode", "invalid" );
34443464

3445-
updateExtents();
3465+
invalidateCachedExtent( false );
34463466
}
34473467

34483468
void QgsOgrProvider::reloadData()

‎src/providers/ogr/qgsogrprovider.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
286286
/** Clean shapefile from features which are marked as deleted */
287287
void repack();
288288

289+
/** Invalidate extent and optionnaly force its low level recomputation */
290+
void invalidateCachedExtent( bool bForceRecomputeExtent );
291+
289292
enum OpenMode
290293
{
291294
OpenModeInitial,
@@ -305,6 +308,7 @@ class QgsOgrProvider : public QgsVectorDataProvider
305308
bool mFirstFieldIsFid;
306309
OGRDataSourceH ogrDataSource;
307310
OGREnvelope* mExtent;
311+
bool mForceRecomputeExtent;
308312

309313
/** This member variable receives the same value as extent_
310314
in the method QgsOgrProvider::extent(). The purpose is to prevent a memory leak*/

‎tests/src/python/test_provider_ogr_gpkg.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import glob
2121
from osgeo import gdal, ogr
2222

23-
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsFeatureRequest
23+
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsFeatureRequest, QgsRectangle
2424
from qgis.testing import start_app, unittest
2525
from utilities import unitTestDataPath
2626

@@ -156,5 +156,44 @@ def testBug15351_closeIter_commit_closeProvider(self):
156156
def testBug15351_commit_closeIter_closeProvider(self):
157157
self.internalTestBug15351('commit_closeIter_closeProvider')
158158

159+
@unittest.expectedFailure(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2))
160+
def testGeopackageExtentUpdate(self):
161+
''' test http://hub.qgis.org/issues/15273 '''
162+
tmpfile = os.path.join(self.basetestpath, 'testGeopackageExtentUpdate.gpkg')
163+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
164+
lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint)
165+
f = ogr.Feature(lyr.GetLayerDefn())
166+
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)'))
167+
lyr.CreateFeature(f)
168+
f = ogr.Feature(lyr.GetLayerDefn())
169+
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)'))
170+
lyr.CreateFeature(f)
171+
f = None
172+
f = ogr.Feature(lyr.GetLayerDefn())
173+
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 0.5)'))
174+
lyr.CreateFeature(f)
175+
f = None
176+
ds = None
177+
178+
vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr')
179+
180+
# Test moving a geometry that touches the bbox
181+
self.assertTrue(vl.startEditing())
182+
self.assertTrue(vl.changeGeometry(1, QgsGeometry.fromWkt('Point (0.5 0)')))
183+
self.assertTrue(vl.commitChanges())
184+
reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 1.0))
185+
provider_extent = QgsGeometry.fromRect(vl.extent())
186+
self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001),
187+
provider_extent.asPolygon()[0])
188+
189+
# Test deleting a geometry that touches the bbox
190+
self.assertTrue(vl.startEditing())
191+
self.assertTrue(vl.deleteFeature(2))
192+
self.assertTrue(vl.commitChanges())
193+
reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 0.5))
194+
provider_extent = QgsGeometry.fromRect(vl.extent())
195+
self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001),
196+
provider_extent.asPolygon()[0])
197+
159198
if __name__ == '__main__':
160199
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.