From 41fa197db166d8c20f69ddac77d99c24971929c1 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Tue, 6 May 2025 12:08:38 +0200 Subject: [PATCH 1/2] [bug] lazy vector extent not stored in the project Fix #61181 When lazily set, if the project was stored without any call to QgsVectorLayer::extent() the QgsMapLayer::mExtent2D was null and not stored in the project. --- src/core/qgsmaplayer.cpp | 11 +++++++-- tests/src/python/test_qgsproject.py | 38 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index 93aa8bbe044..c763f2d4c9c 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -755,8 +755,15 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume if ( !mExtent3D.isNull() && dataProvider() && dataProvider()->elevationProperties() && dataProvider()->elevationProperties()->containsElevationData() ) layerElement.appendChild( QgsXmlUtils::writeBox3D( mExtent3D, document ) ); - else if ( !mExtent2D.isNull() ) - layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent2D, document ) ); + else + { + // Extent might be null because lazily set + const QgsRectangle extent2D { mExtent2D.isNull() ? extent() : mExtent2D }; + if ( !extent2D.isNull() ) + { + layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent2D, document ) ); + } + } if ( const QgsRectangle lWgs84Extent = wgs84Extent( true ); !lWgs84Extent.isNull() ) { diff --git a/tests/src/python/test_qgsproject.py b/tests/src/python/test_qgsproject.py index f2e4d725e4b..6473ea191f6 100644 --- a/tests/src/python/test_qgsproject.py +++ b/tests/src/python/test_qgsproject.py @@ -17,6 +17,7 @@ from io import BytesIO from shutil import copyfile from tempfile import TemporaryDirectory from zipfile import ZipFile +from lxml import etree as et from osgeo import ogr from qgis.PyQt import sip @@ -2119,6 +2120,43 @@ class TestQgsProject(QgisTestCase): del project + def testVectorExtentIsStored(self): + """ + Test that vector layer extent is stored in the project + Test for GH #61181 + """ + + tmpDir = QTemporaryDir() + tmpFile = f"{tmpDir.path()}/project.qgs" + for ext in ["shp", "shx", "dbf"]: + copyfile( + os.path.join(TEST_DATA_DIR, "points." + ext), + os.path.join(tmpDir.path(), "points." + ext), + ) + + project = QgsProject() + + l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr") + #l0.extent() + self.assertTrue(l0.isValid()) + self.assertTrue(project.addMapLayers([l0])) + self.assertTrue(project.write(tmpFile)) + + del project + + # Read the project.qgs as XML using etree and check that the maplayer extent is in the XML file + with open(tmpFile, "r") as f: + xml = f.read() + root = et.XML(xml) + layerXML = root.findall('.//projectlayers/maplayer')[0] + extentXML = layerXML.findall('.//extent')[0] + self.assertNotEqual(len(extentXML.getchildren()), 0) + + + + + + if __name__ == "__main__": unittest.main() From 6c0215fa1198814f6edb9eb34c608bd9a17310ed Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 7 May 2025 07:55:09 +0200 Subject: [PATCH 2/2] Update src/core/qgsmaplayer.cpp Co-authored-by: Even Rouault --- src/core/qgsmaplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index c763f2d4c9c..0f233ba63b6 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -761,7 +761,7 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume const QgsRectangle extent2D { mExtent2D.isNull() ? extent() : mExtent2D }; if ( !extent2D.isNull() ) { - layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent2D, document ) ); + layerElement.appendChild( QgsXmlUtils::writeRectangle( extent2D, document ) ); } }