mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
[processing][GRASS] Correctly handle input vector layers with |layername= param
Fixes #20277
This commit is contained in:
parent
82ac04d6f7
commit
f65c845f86
@ -62,7 +62,8 @@ from qgis.core import (Qgis,
|
|||||||
QgsProcessingParameterFolderDestination,
|
QgsProcessingParameterFolderDestination,
|
||||||
QgsProcessingOutputHtml,
|
QgsProcessingOutputHtml,
|
||||||
QgsProcessingUtils,
|
QgsProcessingUtils,
|
||||||
QgsVectorLayer)
|
QgsVectorLayer,
|
||||||
|
QgsProviderRegistry)
|
||||||
from qgis.utils import iface
|
from qgis.utils import iface
|
||||||
from osgeo import ogr
|
from osgeo import ogr
|
||||||
|
|
||||||
@ -110,12 +111,15 @@ class Grass7Algorithm(QgsProcessingAlgorithm):
|
|||||||
self.params = []
|
self.params = []
|
||||||
self.hardcodedStrings = []
|
self.hardcodedStrings = []
|
||||||
self.inputLayers = []
|
self.inputLayers = []
|
||||||
|
self.commands = []
|
||||||
|
self.outputCommands = []
|
||||||
|
self.exportedLayers = {}
|
||||||
self.descriptionFile = descriptionfile
|
self.descriptionFile = descriptionfile
|
||||||
|
|
||||||
# Default GRASS parameters
|
# Default GRASS parameters
|
||||||
self.region = None
|
self.region = None
|
||||||
self.cellSize = None
|
self.cellSize = None
|
||||||
self.snaptTolerance = None
|
self.snapTolerance = None
|
||||||
self.outputType = None
|
self.outputType = None
|
||||||
self.minArea = None
|
self.minArea = None
|
||||||
self.alignToResolution = None
|
self.alignToResolution = None
|
||||||
@ -801,7 +805,18 @@ class Grass7Algorithm(QgsProcessingAlgorithm):
|
|||||||
:param external: use v.external (v.in.ogr if False).
|
:param external: use v.external (v.in.ogr if False).
|
||||||
"""
|
"""
|
||||||
layer = self.parameterAsVectorLayer(parameters, name, context)
|
layer = self.parameterAsVectorLayer(parameters, name, context)
|
||||||
if layer is None or layer.dataProvider().name() != 'ogr':
|
|
||||||
|
is_ogr_disk_based_layer = layer is not None and layer.dataProvider().name() == 'ogr'
|
||||||
|
if is_ogr_disk_based_layer:
|
||||||
|
# we only support direct reading of disk based ogr layers -- not ogr postgres layers, etc
|
||||||
|
source_parts = QgsProviderRegistry.instance().decodeUri('ogr', layer.source())
|
||||||
|
if not source_parts.get('path'):
|
||||||
|
is_ogr_disk_based_layer = False
|
||||||
|
elif source_parts.get('layerId'):
|
||||||
|
# no support for directly reading layers by id in grass
|
||||||
|
is_ogr_disk_based_layer = False
|
||||||
|
|
||||||
|
if not is_ogr_disk_based_layer:
|
||||||
# parameter is not a vector layer or not an OGR layer - try to convert to a source compatible with
|
# parameter is not a vector layer or not an OGR layer - try to convert to a source compatible with
|
||||||
# grass OGR inputs and extract selection if required
|
# grass OGR inputs and extract selection if required
|
||||||
path = self.parameterAsCompatibleSourceLayerPath(parameters, name, context,
|
path = self.parameterAsCompatibleSourceLayerPath(parameters, name, context,
|
||||||
@ -827,28 +842,36 @@ class Grass7Algorithm(QgsProcessingAlgorithm):
|
|||||||
external = ProcessingConfig.getSetting(
|
external = ProcessingConfig.getSetting(
|
||||||
Grass7Utils.GRASS_USE_VEXTERNAL)
|
Grass7Utils.GRASS_USE_VEXTERNAL)
|
||||||
|
|
||||||
|
source_parts = QgsProviderRegistry.instance().decodeUri('ogr', layer.source())
|
||||||
|
file_path = source_parts.get('path')
|
||||||
|
layer_name = source_parts.get('layerName')
|
||||||
|
|
||||||
# safety check: we can only use external for ogr layers which support random read
|
# safety check: we can only use external for ogr layers which support random read
|
||||||
if external:
|
if external:
|
||||||
feedback.pushInfo('Attempting to use v.external for direct layer read')
|
if feedback is not None:
|
||||||
|
feedback.pushInfo('Attempting to use v.external for direct layer read')
|
||||||
ds = ogr.Open(file_path)
|
ds = ogr.Open(file_path)
|
||||||
if ds is not None:
|
if ds is not None:
|
||||||
ogr_layer = ds.GetLayer()
|
ogr_layer = ds.GetLayer()
|
||||||
if ogr_layer is None or not ogr_layer.TestCapability(ogr.OLCRandomRead):
|
if ogr_layer is None or not ogr_layer.TestCapability(ogr.OLCRandomRead):
|
||||||
feedback.reportError('Cannot use v.external: layer does not support random read')
|
if feedback is not None:
|
||||||
|
feedback.reportError('Cannot use v.external: layer does not support random read')
|
||||||
external = False
|
external = False
|
||||||
else:
|
else:
|
||||||
feedback.reportError('Cannot use v.external: error reading layer')
|
if feedback is not None:
|
||||||
|
feedback.reportError('Cannot use v.external: error reading layer')
|
||||||
external = False
|
external = False
|
||||||
|
|
||||||
self.inputLayers.append(layer)
|
self.inputLayers.append(layer)
|
||||||
self.setSessionProjectionFromLayer(layer)
|
self.setSessionProjectionFromLayer(layer)
|
||||||
destFilename = 'vector_{}'.format(os.path.basename(getTempFilename()))
|
destFilename = 'vector_{}'.format(os.path.basename(getTempFilename()))
|
||||||
self.exportedLayers[name] = destFilename
|
self.exportedLayers[name] = destFilename
|
||||||
command = '{0}{1}{2} input="{3}" output="{4}" --overwrite -o'.format(
|
command = '{0}{1}{2} input="{3}"{4} output="{5}" --overwrite -o'.format(
|
||||||
'v.external' if external else 'v.in.ogr',
|
'v.external' if external else 'v.in.ogr',
|
||||||
' min_area={}'.format(self.minArea) if not external else '',
|
' min_area={}'.format(self.minArea) if not external else '',
|
||||||
' snap={}'.format(self.snapTolerance) if not external else '',
|
' snap={}'.format(self.snapTolerance) if not external else '',
|
||||||
os.path.normpath(layer.source()),
|
os.path.normpath(file_path),
|
||||||
|
' layer="{}"'.format(layer_name) if layer_name else '',
|
||||||
destFilename)
|
destFilename)
|
||||||
self.commands.append(command)
|
self.commands.append(command)
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import nose2
|
|||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import re
|
||||||
|
|
||||||
from qgis.core import (QgsVectorLayer,
|
from qgis.core import (QgsVectorLayer,
|
||||||
QgsApplication,
|
QgsApplication,
|
||||||
@ -48,6 +49,9 @@ from qgis.testing import (
|
|||||||
from processing.algs.grass7.Grass7Utils import Grass7Utils
|
from processing.algs.grass7.Grass7Utils import Grass7Utils
|
||||||
|
|
||||||
|
|
||||||
|
testDataPath = os.path.join(os.path.dirname(__file__), 'testdata')
|
||||||
|
|
||||||
|
|
||||||
class TestGrass7AlgorithmsVectorTest(unittest.TestCase, AlgorithmsTestBase.AlgorithmsTest):
|
class TestGrass7AlgorithmsVectorTest(unittest.TestCase, AlgorithmsTestBase.AlgorithmsTest):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -241,6 +245,65 @@ class TestGrass7AlgorithmsVectorTest(unittest.TestCase, AlgorithmsTestBase.Algor
|
|||||||
|
|
||||||
QgsProject.instance().removeMapLayer(layer)
|
QgsProject.instance().removeMapLayer(layer)
|
||||||
|
|
||||||
|
def testVectorLayerInput(self):
|
||||||
|
alg = QgsApplication.processingRegistry().createAlgorithmById('grass7:v.buffer')
|
||||||
|
self.assertIsNotNone(alg)
|
||||||
|
self.assertFalse(alg.commands)
|
||||||
|
|
||||||
|
def get_command(alg):
|
||||||
|
command = alg.commands[-1]
|
||||||
|
command = re.sub(r'output=".*?"', 'output="###"', command)
|
||||||
|
command = command.replace(testDataPath, 'testdata')
|
||||||
|
return command
|
||||||
|
|
||||||
|
# GML source
|
||||||
|
source = os.path.join(testDataPath, 'points.gml')
|
||||||
|
vl = QgsVectorLayer(source)
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=False)
|
||||||
|
self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/points.gml" output="###" --overwrite -o')
|
||||||
|
# try with external -- not support for GML, so should fall back to v.in.ogr
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=True)
|
||||||
|
self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/points.gml" output="###" --overwrite -o')
|
||||||
|
|
||||||
|
# SHP source
|
||||||
|
source = os.path.join(testDataPath, 'lines_z.shp')
|
||||||
|
vl = QgsVectorLayer(source)
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=False)
|
||||||
|
self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/lines_z.shp" output="###" --overwrite -o')
|
||||||
|
# try with external -- should work for shapefile
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=True)
|
||||||
|
self.assertEqual(get_command(alg), 'v.external input="testdata/lines_z.shp" output="###" --overwrite -o')
|
||||||
|
|
||||||
|
# GPKG source
|
||||||
|
source = os.path.join(testDataPath, 'custom/pol.gpkg')
|
||||||
|
vl = QgsVectorLayer(source + '|layername=pol2')
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=False)
|
||||||
|
self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/custom/pol.gpkg" layer="pol2" output="###" --overwrite -o')
|
||||||
|
# try with external -- should work for Geopackage (although grass itself tends to crash here!)
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=True)
|
||||||
|
self.assertEqual(get_command(alg), 'v.external input="testdata/custom/pol.gpkg" layer="pol2" output="###" --overwrite -o')
|
||||||
|
|
||||||
|
# different layer
|
||||||
|
source = os.path.join(testDataPath, 'custom/pol.gpkg')
|
||||||
|
vl = QgsVectorLayer(source + '|layername=pol3')
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=False)
|
||||||
|
self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/custom/pol.gpkg" layer="pol3" output="###" --overwrite -o')
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=True)
|
||||||
|
self.assertEqual(get_command(alg), 'v.external input="testdata/custom/pol.gpkg" layer="pol3" output="###" --overwrite -o')
|
||||||
|
|
||||||
|
# GPKG no layer: you get what you get and you don't get upset
|
||||||
|
source = os.path.join(testDataPath, 'custom/pol.gpkg')
|
||||||
|
vl = QgsVectorLayer(source)
|
||||||
|
self.assertTrue(vl.isValid())
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=False)
|
||||||
|
self.assertEqual(get_command(alg), 'v.in.ogr min_area=None snap=None input="testdata/custom/pol.gpkg" output="###" --overwrite -o')
|
||||||
|
alg.loadVectorLayer('test_layer', vl, external=True)
|
||||||
|
self.assertEqual(get_command(alg), 'v.external input="testdata/custom/pol.gpkg" output="###" --overwrite -o')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
nose2.main()
|
nose2.main()
|
||||||
|
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.dbf
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.dbf
vendored
Normal file
Binary file not shown.
1
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.prj
vendored
Normal file
1
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.prj
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
PROJCS["Hotine_Oblique_Mercator_Azimuth_Center",GEOGCS["GCS_bessel",DATUM["D_unknown",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Hotine_Oblique_Mercator_Azimuth_Center"],PARAMETER["latitude_of_center",46.95240555555556],PARAMETER["longitude_of_center",7.439583333333333],PARAMETER["azimuth",90],PARAMETER["scale_factor",1],PARAMETER["false_easting",2600000],PARAMETER["false_northing",1200000],UNIT["Meter",1]]
|
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.shp
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.shp
vendored
Normal file
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.shx
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer2.shx
vendored
Normal file
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.dbf
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.dbf
vendored
Normal file
Binary file not shown.
1
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.prj
vendored
Normal file
1
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.prj
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
PROJCS["Hotine_Oblique_Mercator_Azimuth_Center",GEOGCS["GCS_bessel",DATUM["D_unknown",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Hotine_Oblique_Mercator_Azimuth_Center"],PARAMETER["latitude_of_center",46.95240555555556],PARAMETER["longitude_of_center",7.439583333333333],PARAMETER["azimuth",90],PARAMETER["scale_factor",1],PARAMETER["false_easting",2600000],PARAMETER["false_northing",1200000],UNIT["Meter",1]]
|
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.shp
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.shp
vendored
Normal file
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.shx
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/grass7/buffer_polys_layer3.shx
vendored
Normal file
Binary file not shown.
@ -32,6 +32,7 @@ tests:
|
|||||||
compare:
|
compare:
|
||||||
geometry:
|
geometry:
|
||||||
precision: 7
|
precision: 7
|
||||||
|
ignore_crs_check: true
|
||||||
|
|
||||||
- algorithm: grass7:v.rast.stats
|
- algorithm: grass7:v.rast.stats
|
||||||
name: V.rast.stats
|
name: V.rast.stats
|
||||||
@ -271,3 +272,62 @@ tests:
|
|||||||
raster_output:
|
raster_output:
|
||||||
hash: 7aa8e68b697e1558e6621fa23b5f1f01a5826649ffc31fd255e709a1
|
hash: 7aa8e68b697e1558e6621fa23b5f1f01a5826649ffc31fd255e709a1
|
||||||
type: rasterhash
|
type: rasterhash
|
||||||
|
|
||||||
|
- algorithm: grass7:v.buffer
|
||||||
|
name: Buffer with layername
|
||||||
|
params:
|
||||||
|
-c: false
|
||||||
|
-s: false
|
||||||
|
-t: false
|
||||||
|
GRASS_MIN_AREA_PARAMETER: 0.0001
|
||||||
|
GRASS_OUTPUT_TYPE_PARAMETER: 0
|
||||||
|
GRASS_SNAP_TOLERANCE_PARAMETER: -1.0
|
||||||
|
GRASS_VECTOR_DSCO: ''
|
||||||
|
GRASS_VECTOR_LCO: ''
|
||||||
|
angle: 0.0
|
||||||
|
cats: ''
|
||||||
|
distance: 10.0
|
||||||
|
input:
|
||||||
|
name: custom/pol.gpkg|layername=pol3
|
||||||
|
type: vector
|
||||||
|
scale: 1.0
|
||||||
|
tolerance: 0.01
|
||||||
|
type:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
- 4
|
||||||
|
where: ''
|
||||||
|
results:
|
||||||
|
output:
|
||||||
|
name: expected/grass7/buffer_polys_layer3.shp
|
||||||
|
type: vector
|
||||||
|
|
||||||
|
- algorithm: grass7:v.buffer
|
||||||
|
name: Buffer with layername 2
|
||||||
|
params:
|
||||||
|
-c: false
|
||||||
|
-s: false
|
||||||
|
-t: false
|
||||||
|
GRASS_MIN_AREA_PARAMETER: 0.0001
|
||||||
|
GRASS_OUTPUT_TYPE_PARAMETER: 0
|
||||||
|
GRASS_SNAP_TOLERANCE_PARAMETER: -1.0
|
||||||
|
GRASS_VECTOR_DSCO: ''
|
||||||
|
GRASS_VECTOR_LCO: ''
|
||||||
|
angle: 0.0
|
||||||
|
cats: ''
|
||||||
|
distance: 10.0
|
||||||
|
input:
|
||||||
|
name: custom/pol.gpkg|layername=pol2
|
||||||
|
type: vector
|
||||||
|
scale: 1.0
|
||||||
|
tolerance: 0.01
|
||||||
|
type:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
- 4
|
||||||
|
where: ''
|
||||||
|
results:
|
||||||
|
output:
|
||||||
|
name: expected/grass7/buffer_polys_layer2.shp
|
||||||
|
type: vector
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user