From e0f781edefe278a81bdedb2b9ef2d4eb60d93233 Mon Sep 17 00:00:00 2001 From: laiskasiili Date: Thu, 12 Sep 2024 16:58:20 +0200 Subject: [PATCH 1/5] Attempt to fix doctests by using airports layer in epsg4326 instead of epsg2964. Use geopackage airports layer (epsg4326) to avoid ambiguous reprojection warning of previously used Alaska scoped airports layer (epsg2964) when adding it programmatically to a vanilla qgis project during doctest. We do not simply reproject the layer, because the alaska airports crs is mentioned mutliple times in the codebase and streamlining the whole cookbook is out of scope for this PR. --- docs/pyqgis_developer_cookbook/canvas.rst | 7 +-- .../pyqgis_developer_cookbook/cheat_sheet.rst | 2 +- docs/pyqgis_developer_cookbook/loadlayer.rst | 61 ++++++------------- docs/pyqgis_developer_cookbook/vector.rst | 31 ++++++---- 4 files changed, 40 insertions(+), 61 deletions(-) diff --git a/docs/pyqgis_developer_cookbook/canvas.rst b/docs/pyqgis_developer_cookbook/canvas.rst index f4479fcbcf1..94165768898 100644 --- a/docs/pyqgis_developer_cookbook/canvas.rst +++ b/docs/pyqgis_developer_cookbook/canvas.rst @@ -123,7 +123,7 @@ layers for the canvas. .. testcode:: canvas - vlayer = QgsVectorLayer('testdata/airports.shp', "Airports layer", "ogr") + vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") if not vlayer.isValid(): print("Layer failed to load!") @@ -136,11 +136,6 @@ layers for the canvas. # set the map canvas layer set canvas.setLayers([vlayer]) -.. testoutput:: canvas - :hide: - - (2): Using non-preferred coordinate operation between EPSG:2964 and EPSG:4326. Using +proj=pipeline +step +proj=unitconvert +xy_in=us-ft +xy_out=m +step +inv +proj=aea +lat_0=50 +lon_0=-154 +lat_1=55 +lat_2=65 +x_0=0 +y_0=0 +ellps=clrk66 +step +proj=push +v_3 +step +proj=cart +ellps=clrk66 +step +proj=helmert +x=-5 +y=135 +z=172 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg, preferred +proj=pipeline +step +proj=unitconvert +xy_in=us-ft +xy_out=m +step +inv +proj=aea +lat_0=50 +lon_0=-154 +lat_1=55 +lat_2=65 +x_0=0 +y_0=0 +ellps=clrk66 +step +proj=hgridshift +grids=us_noaa_alaska.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg. - After executing these commands, the canvas should show the layer you have loaded. diff --git a/docs/pyqgis_developer_cookbook/cheat_sheet.rst b/docs/pyqgis_developer_cookbook/cheat_sheet.rst index fa03c69367f..e032cd9dd93 100644 --- a/docs/pyqgis_developer_cookbook/cheat_sheet.rst +++ b/docs/pyqgis_developer_cookbook/cheat_sheet.rst @@ -168,7 +168,7 @@ Layers .. testcode:: cheat_sheet - layer = iface.addVectorLayer("testdata/airports.shp", "layer name you like", "ogr") + layer = iface.addVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") if not layer or not layer.isValid(): print("Layer failed to load!") diff --git a/docs/pyqgis_developer_cookbook/loadlayer.rst b/docs/pyqgis_developer_cookbook/loadlayer.rst index 5ac9389d1b7..58330bfec03 100644 --- a/docs/pyqgis_developer_cookbook/loadlayer.rst +++ b/docs/pyqgis_developer_cookbook/loadlayer.rst @@ -49,28 +49,7 @@ Vector Layers ============= To create and add a vector layer instance to the project, specify the layer's data source -identifier, name for the layer and provider's name: - -.. testcode:: loadlayer - - # get the path to the shapefile e.g. /home/project/data/ports.shp - path_to_airports_layer = "testdata/airports.shp" - - # The format is: - # vlayer = QgsVectorLayer(data_source, layer_name, provider_name) - - vlayer = QgsVectorLayer(path_to_airports_layer, "Airports layer", "ogr") - if not vlayer.isValid(): - print("Layer failed to load!") - else: - QgsProject.instance().addMapLayer(vlayer) - -.. .. testoutput:: loadlayer - :hide: - - (2): Using non-preferred coordinate operation between EPSG:2964 and EPSG:4326. Using +proj=pipeline +step +proj=unitconvert +xy_in=us-ft +xy_out=m +step +inv +proj=aea +lat_0=50 +lon_0=-154 +lat_1=55 +lat_2=65 +x_0=0 +y_0=0 +ellps=clrk66 +step +proj=push +v_3 +step +proj=cart +ellps=clrk66 +step +proj=helmert +x=-5 +y=135 +z=172 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg, preferred +proj=pipeline +step +proj=unitconvert +xy_in=us-ft +xy_out=m +step +inv +proj=aea +lat_0=50 +lon_0=-154 +lat_1=55 +lat_2=65 +x_0=0 +y_0=0 +ellps=clrk66 +step +proj=hgridshift +grids=us_noaa_alaska.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg. - -The data source identifier is a string and it is specific to each vector data +identifier. The data source identifier is a string and it is specific to each vector data provider. Layer's name is used in the layer list widget. It is important to check whether the layer has been loaded successfully. If it was not, an invalid layer instance is returned. @@ -79,12 +58,11 @@ For a geopackage vector layer: .. testcode:: loadlayer - # get the path to a geopackage e.g. /usr/share/qgis/resources/data/world_map.gpkg - path_to_gpkg = os.path.join(QgsApplication.pkgDataPath(), "resources", "data", "world_map.gpkg") + # get the path to a geopackage + path_to_gpkg = "testdata/data/data.gpkg" # append the layername part - gpkg_countries_layer = path_to_gpkg + "|layername=countries" - # e.g. gpkg_places_layer = "/usr/share/qgis/resources/data/world_map.gpkg|layername=countries" - vlayer = QgsVectorLayer(gpkg_countries_layer, "Countries layer", "ogr") + gpkg_airports_layer = path_to_gpkg + "|layername=airports" + vlayer = QgsVectorLayer(gpkg_airports_layer, "Airports layer", "ogr") if not vlayer.isValid(): print("Layer failed to load!") else: @@ -96,7 +74,7 @@ method of the :class:`QgisInterface `: .. testcode:: loadlayer - vlayer = iface.addVectorLayer(path_to_airports_layer, "Airports layer", "ogr") + vlayer = iface.addVectorLayer(gpkg_airports_layer, "Airports layer", "ogr") if not vlayer: print("Layer failed to load!") @@ -110,23 +88,27 @@ providers: .. index:: pair: Loading; GDAL layers -* GDAL library (Shapefile and many other file formats) --- data source is the - path to the file: +* The ogr provider from the GDAL library supports a `wide variety of formats `_, + also called drivers in GDAL speak. + Examples are Geopackage, Flatgeobuf, Geojson and also The-One-We-Shall-Not-Name. + For single-file formats the filepath usually suffices as uri. + For geopackages or dxf, a pipe separated suffix allows to specify the layername to load. - * for Shapefile: + * for Geopackage: .. testcode:: loadlayer - vlayer = QgsVectorLayer("testdata/airports.shp", "layer_name_you_like", "ogr") - QgsProject.instance().addMapLayer(vlayer) + uri = "testdata/data/data.gpkg|layername=airports" + vlayer = QgsVectorLayer(uri, "layer_name_you_like", "ogr") + QgsProject.instance().addMapLayer(vlayer) * for dxf (note the internal options in data source uri): .. testcode:: loadlayer - uri = "testdata/sample.dxf|layername=entities|geometrytype=Polygon" - vlayer = QgsVectorLayer(uri, "layer_name_you_like", "ogr") - QgsProject.instance().addMapLayer(vlayer) + uri = "testdata/sample.dxf|layername=entities|geometrytype=Polygon" + vlayer = QgsVectorLayer(uri, "layer_name_you_like", "ogr") + QgsProject.instance().addMapLayer(vlayer) .. index:: pair: Loading; PostGIS layers @@ -227,14 +209,9 @@ providers: .. testcode:: loadlayer - uri = "https://demo.mapserver.org/cgi-bin/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=ms:cities" + uri = "https://demo.mapserver.org/cgi-bin/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&SRSNAME=EPSG:4326&TYPENAME=ms:cities" vlayer = QgsVectorLayer(uri, "my wfs layer", "WFS") - .. .. testoutput:: loadlayer - :hide: - - (2): Using non-preferred coordinate operation between EPSG:2964 and EPSG:4326. Using +proj=pipeline +step +proj=unitconvert +xy_in=us-ft +xy_out=m +step +inv +proj=aea +lat_0=50 +lon_0=-154 +lat_1=55 +lat_2=65 +x_0=0 +y_0=0 +ellps=clrk66 +step +proj=push +v_3 +step +proj=cart +ellps=clrk66 +step +proj=helmert +x=-5 +y=135 +z=172 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg, preferred +proj=pipeline +step +proj=unitconvert +xy_in=us-ft +xy_out=m +step +inv +proj=aea +lat_0=50 +lon_0=-154 +lat_1=55 +lat_2=65 +x_0=0 +y_0=0 +ellps=clrk66 +step +proj=hgridshift +grids=us_noaa_alaska.tif +step +proj=unitconvert +xy_in=rad +xy_out=deg. - The uri can be created using the standard ``urllib`` library: .. testcode:: loadlayer diff --git a/docs/pyqgis_developer_cookbook/vector.rst b/docs/pyqgis_developer_cookbook/vector.rst index a8a017ead46..567ef85943c 100644 --- a/docs/pyqgis_developer_cookbook/vector.rst +++ b/docs/pyqgis_developer_cookbook/vector.rst @@ -73,18 +73,25 @@ by calling :meth:`fields() ` on a .. testcode:: vectors - vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr") + vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") for field in vlayer.fields(): print(field.name(), field.typeName()) .. testoutput:: vectors - ID Integer64 - fk_region Integer64 - ELEV Real - NAME String - USE String + fid Integer64 + id Integer64 + scalerank Integer64 + featurecla String + type String + name String + abbrev String + location String + gps_code String + iata_code String + wikipedia String + natlscale Real The :meth:`displayField() ` and :meth:`mapTipTemplate() ` methods provide @@ -96,13 +103,13 @@ methods you can easily get both: .. testcode:: vectors - vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr") + vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") print(vlayer.displayField()) .. testoutput:: vectors - NAME + name .. note:: If you change the ``Display Name`` from a field to an expression, you have to use :meth:`displayExpression() ` @@ -703,7 +710,7 @@ field: .. testcode:: vectors - vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr") + vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") feat = QgsVectorLayerUtils.createFeature(vlayer) @@ -712,7 +719,7 @@ you to quickly get the values of a field or expression: .. testcode:: vectors - vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr") + vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") # select only the first feature to make the output shorter vlayer.selectByIds([1]) val = QgsVectorLayerUtils.getValues(vlayer, "NAME", selectedOnly=True) @@ -720,7 +727,7 @@ you to quickly get the values of a field or expression: .. testoutput:: vectors - (['AMBLER'], True) + (['Sahnewal'], True) .. index:: Vector layers; Creating @@ -1230,7 +1237,7 @@ arrangement) from qgis.PyQt import QtGui - myVectorLayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr") + myVectorLayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") myTargetField = 'ELEV' myRangeList = [] myOpacity = 1 From b4d17c19f2a143de87b9ceca2c3073cc705026da Mon Sep 17 00:00:00 2001 From: laiskasiili Date: Thu, 12 Sep 2024 17:04:14 +0200 Subject: [PATCH 2/5] Revert capitalized parameters for wfs uri to be similar to subsequent examples. --- docs/pyqgis_developer_cookbook/loadlayer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pyqgis_developer_cookbook/loadlayer.rst b/docs/pyqgis_developer_cookbook/loadlayer.rst index 58330bfec03..dfa4cf58074 100644 --- a/docs/pyqgis_developer_cookbook/loadlayer.rst +++ b/docs/pyqgis_developer_cookbook/loadlayer.rst @@ -209,7 +209,7 @@ providers: .. testcode:: loadlayer - uri = "https://demo.mapserver.org/cgi-bin/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&SRSNAME=EPSG:4326&TYPENAME=ms:cities" + uri = "https://demo.mapserver.org/cgi-bin/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=ms:cities" vlayer = QgsVectorLayer(uri, "my wfs layer", "WFS") The uri can be created using the standard ``urllib`` library: From 3aed5bf7d062a99c6cdfa55a1eb3eefd1fc98a49 Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Fri, 29 Nov 2024 23:02:26 +0100 Subject: [PATCH 3/5] Address review --- docs/pyqgis_developer_cookbook/loadlayer.rst | 29 +++++++++++++------- docs/pyqgis_developer_cookbook/vector.rst | 2 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/docs/pyqgis_developer_cookbook/loadlayer.rst b/docs/pyqgis_developer_cookbook/loadlayer.rst index dfa4cf58074..092b5ca97d3 100644 --- a/docs/pyqgis_developer_cookbook/loadlayer.rst +++ b/docs/pyqgis_developer_cookbook/loadlayer.rst @@ -48,11 +48,11 @@ them here. Vector Layers ============= -To create and add a vector layer instance to the project, specify the layer's data source -identifier. The data source identifier is a string and it is specific to each vector data -provider. Layer's name is used in the layer list widget. It is important to -check whether the layer has been loaded successfully. If it was not, an invalid -layer instance is returned. +To create and add a vector layer instance to the project, specify the layer's data source identifier. +The data source identifier is a string and it is specific to each vector data provider. +An optional layer name is used for identifying the layer in the :guilabel:`Layers` panel. +It is important to check whether the layer has been loaded successfully. +If it was not, an invalid layer instance is returned. For a geopackage vector layer: @@ -70,7 +70,7 @@ For a geopackage vector layer: The quickest way to open and display a vector layer in QGIS is the :meth:`addVectorLayer() ` -method of the :class:`QgisInterface `: +method of the :class:`QgisInterface ` class: .. testcode:: loadlayer @@ -88,13 +88,22 @@ providers: .. index:: pair: Loading; GDAL layers -* The ogr provider from the GDAL library supports a `wide variety of formats `_, +* The ogr provider from the GDAL library supports a `wide variety of formats + `_, also called drivers in GDAL speak. - Examples are Geopackage, Flatgeobuf, Geojson and also The-One-We-Shall-Not-Name. + Examples are ESRI Shapefile, Geopackage, Flatgeobuf, Geojson, ... For single-file formats the filepath usually suffices as uri. - For geopackages or dxf, a pipe separated suffix allows to specify the layername to load. + For geopackages or dxf, a pipe separated suffix allows to specify the layer to load. - * for Geopackage: + * for ESRI Shapefile: + + .. code:: + + uri = "testdata/airports.shp" + vlayer = QgsVectorLayer(uri, "layer_name_you_like", "ogr") + QgsProject.instance().addMapLayer(vlayer) + + * for Geopackage (note the internal options in data source uri): .. testcode:: loadlayer diff --git a/docs/pyqgis_developer_cookbook/vector.rst b/docs/pyqgis_developer_cookbook/vector.rst index 567ef85943c..5f96946f500 100644 --- a/docs/pyqgis_developer_cookbook/vector.rst +++ b/docs/pyqgis_developer_cookbook/vector.rst @@ -1238,7 +1238,7 @@ arrangement) from qgis.PyQt import QtGui myVectorLayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr") - myTargetField = 'ELEV' + myTargetField = 'scalerank' myRangeList = [] myOpacity = 1 # Make our first symbol and range... From 46fc56837df2fbf4f438491afee7956161b5837a Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Fri, 29 Nov 2024 23:11:47 +0100 Subject: [PATCH 4/5] Fix expression error string --- docs/pyqgis_developer_cookbook/expressions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pyqgis_developer_cookbook/expressions.rst b/docs/pyqgis_developer_cookbook/expressions.rst index 7868af54eee..8423c0407ed 100644 --- a/docs/pyqgis_developer_cookbook/expressions.rst +++ b/docs/pyqgis_developer_cookbook/expressions.rst @@ -96,7 +96,7 @@ The following example shows how to check if a given expression can be parsed cor exp = QgsExpression('1 + 1 = ') assert(exp.hasParserError()) - assert(exp.parserErrorString() == '\nsyntax error, unexpected end of file') + assert(exp.parserErrorString() == '\nIncomplete expression. You might not have finished the full expression.') .. index:: Expressions; Evaluating From eca54a3fa0b8c3091f0e4cd08703a36d8f5a02ec Mon Sep 17 00:00:00 2001 From: Harrissou Sant-anna Date: Sat, 30 Nov 2024 09:09:48 +0100 Subject: [PATCH 5/5] Revert "Fix expression error string" This reverts commit 46fc56837df2fbf4f438491afee7956161b5837a. The change was not backported to 3.40 --- docs/pyqgis_developer_cookbook/expressions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pyqgis_developer_cookbook/expressions.rst b/docs/pyqgis_developer_cookbook/expressions.rst index 8423c0407ed..7868af54eee 100644 --- a/docs/pyqgis_developer_cookbook/expressions.rst +++ b/docs/pyqgis_developer_cookbook/expressions.rst @@ -96,7 +96,7 @@ The following example shows how to check if a given expression can be parsed cor exp = QgsExpression('1 + 1 = ') assert(exp.hasParserError()) - assert(exp.parserErrorString() == '\nIncomplete expression. You might not have finished the full expression.') + assert(exp.parserErrorString() == '\nsyntax error, unexpected end of file') .. index:: Expressions; Evaluating