2014-04-19 15:53:43 +02:00
# -*- coding: utf-8 -*-
"""
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Grass7Algorithm . py
- - - - - - - - - - - - - - - - - - - - -
2015-02-07 21:53:30 +01:00
Date : February 2015
Copyright : ( C ) 2014 - 2015 by Victor Olaya
2014-04-19 15:53:43 +02:00
Email : volayaf at gmail dot com
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"""
__author__ = ' Victor Olaya '
2015-02-07 21:53:30 +01:00
__date__ = ' February 2015 '
__copyright__ = ' (C) 2012-2015, Victor Olaya '
2014-04-19 15:53:43 +02:00
2017-09-03 17:42:13 +02:00
import sys
2014-04-19 15:53:43 +02:00
import os
2017-12-14 11:21:01 +02:00
import re
2014-04-19 15:53:43 +02:00
import uuid
import importlib
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
2016-04-22 10:38:48 +02:00
from qgis . PyQt . QtCore import QCoreApplication , QUrl
2015-07-26 03:48:27 +02:00
2018-02-05 22:11:34 -04:00
from qgis . core import ( Qgis ,
QgsRasterLayer ,
2017-04-24 14:35:50 +10:00
QgsApplication ,
2019-03-27 07:20:43 +10:00
QgsMapLayerType ,
2019-04-12 12:46:03 +02:00
QgsCoordinateReferenceSystem ,
2017-04-24 14:35:50 +10:00
QgsProcessingUtils ,
2017-10-22 10:25:09 +02:00
QgsProcessing ,
2017-05-16 15:21:41 +10:00
QgsMessageLog ,
2017-09-06 14:15:24 +02:00
QgsVectorFileWriter ,
2017-05-16 16:36:00 +10:00
QgsProcessingAlgorithm ,
2017-09-01 17:53:33 +02:00
QgsProcessingParameterDefinition ,
QgsProcessingException ,
QgsProcessingParameterExtent ,
2017-09-04 16:31:31 +02:00
QgsProcessingParameterEnum ,
2017-09-03 17:42:13 +02:00
QgsProcessingParameterNumber ,
QgsProcessingParameterString ,
2017-10-22 10:25:09 +02:00
QgsProcessingParameterField ,
2017-09-03 17:42:13 +02:00
QgsProcessingParameterPoint ,
QgsProcessingParameterBoolean ,
2018-04-08 12:23:15 +10:00
QgsProcessingParameterFeatureSource ,
2017-09-03 17:42:13 +02:00
QgsProcessingParameterVectorLayer ,
QgsProcessingParameterRasterLayer ,
QgsProcessingParameterMultipleLayers ,
2017-09-04 16:31:31 +02:00
QgsProcessingParameterVectorDestination ,
QgsProcessingParameterRasterDestination ,
2017-09-06 14:15:24 +02:00
QgsProcessingParameterFileDestination ,
2017-12-27 20:13:05 +01:00
QgsProcessingParameterFile ,
2017-10-25 21:29:28 +02:00
QgsProcessingParameterFolderDestination ,
2018-03-26 16:22:30 +10:00
QgsProcessingOutputHtml ,
2018-11-01 07:56:15 +10:00
QgsVectorLayer ,
QgsProviderRegistry )
2014-05-20 16:30:59 +02:00
from qgis . utils import iface
2019-03-28 10:25:30 +10:00
import warnings
with warnings . catch_warnings ( ) :
warnings . filterwarnings ( " ignore " , category = DeprecationWarning )
from osgeo import ogr
2014-04-19 15:53:43 +02:00
from processing . core . ProcessingConfig import ProcessingConfig
2019-03-04 21:14:14 +01:00
from processing . core . parameters import getParameterFromString
2014-04-19 15:53:43 +02:00
2016-03-21 04:58:12 +01:00
from . Grass7Utils import Grass7Utils
2014-04-19 15:53:43 +02:00
2017-09-11 18:19:56 +02:00
from processing . tools . system import isWindows , getTempFilename
2014-04-19 15:53:43 +02:00
2015-05-18 19:51:26 +03:00
pluginPath = os . path . normpath ( os . path . join (
os . path . split ( os . path . dirname ( __file__ ) ) [ 0 ] , os . pardir ) )
2014-04-19 15:53:43 +02:00
2017-09-01 17:53:33 +02:00
class Grass7Algorithm ( QgsProcessingAlgorithm ) :
2014-04-19 15:53:43 +02:00
GRASS_OUTPUT_TYPE_PARAMETER = ' GRASS_OUTPUT_TYPE_PARAMETER '
GRASS_MIN_AREA_PARAMETER = ' GRASS_MIN_AREA_PARAMETER '
GRASS_SNAP_TOLERANCE_PARAMETER = ' GRASS_SNAP_TOLERANCE_PARAMETER '
GRASS_REGION_EXTENT_PARAMETER = ' GRASS_REGION_PARAMETER '
GRASS_REGION_CELLSIZE_PARAMETER = ' GRASS_REGION_CELLSIZE_PARAMETER '
2017-10-22 10:25:09 +02:00
GRASS_REGION_ALIGN_TO_RESOLUTION = ' GRASS_REGION_ALIGN_TO_RESOLUTION '
2017-11-05 13:18:24 +01:00
GRASS_RASTER_FORMAT_OPT = ' GRASS_RASTER_FORMAT_OPT '
GRASS_RASTER_FORMAT_META = ' GRASS_RASTER_FORMAT_META '
2018-01-27 10:26:28 +02:00
GRASS_VECTOR_DSCO = ' GRASS_VECTOR_DSCO '
GRASS_VECTOR_LCO = ' GRASS_VECTOR_LCO '
2018-11-05 12:42:12 +00:00
GRASS_VECTOR_EXPORT_NOCAT = ' GRASS_VECTOR_EXPORT_NOCAT '
2014-04-19 15:53:43 +02:00
OUTPUT_TYPES = [ ' auto ' , ' point ' , ' line ' , ' area ' ]
2017-10-22 10:25:09 +02:00
QGIS_OUTPUT_TYPES = { QgsProcessing . TypeVectorAnyGeometry : ' auto ' ,
QgsProcessing . TypeVectorPoint : ' point ' ,
QgsProcessing . TypeVectorLine : ' line ' ,
QgsProcessing . TypeVectorPolygon : ' area ' }
2014-04-19 15:53:43 +02:00
def __init__ ( self , descriptionfile ) :
2017-09-01 17:53:33 +02:00
super ( ) . __init__ ( )
2017-03-29 12:51:59 +10:00
self . _name = ' '
self . _display_name = ' '
2018-05-16 19:22:12 +10:00
self . _short_description = ' '
2017-03-29 12:04:09 +10:00
self . _group = ' '
2017-12-14 11:21:01 +02:00
self . _groupId = ' '
2018-06-05 18:44:37 +01:00
self . groupIdRegex = re . compile ( r ' ^[^ \ s \ (]+ ' )
2017-09-01 17:53:33 +02:00
self . grass7Name = ' '
2017-09-03 17:42:13 +02:00
self . params = [ ]
2015-10-22 09:44:19 +02:00
self . hardcodedStrings = [ ]
2017-10-22 10:25:09 +02:00
self . inputLayers = [ ]
2018-11-01 07:56:15 +10:00
self . commands = [ ]
self . outputCommands = [ ]
self . exportedLayers = { }
2014-04-19 15:53:43 +02:00
self . descriptionFile = descriptionfile
2017-09-06 14:15:24 +02:00
# Default GRASS parameters
self . region = None
self . cellSize = None
2018-11-01 07:56:15 +10:00
self . snapTolerance = None
2017-09-06 14:15:24 +02:00
self . outputType = None
self . minArea = None
self . alignToResolution = None
2017-10-22 10:25:09 +02:00
2019-04-12 12:46:03 +02:00
# destination Crs for combineLayerExtents, will be set from layer or mapSettings
self . destination_crs = QgsCoordinateReferenceSystem ( )
2017-09-04 16:31:31 +02:00
# Load parameters from a description file
2014-04-19 15:53:43 +02:00
self . defineCharacteristicsFromFile ( )
self . numExportedLayers = 0
2017-09-04 16:31:31 +02:00
# Do we need this anymore?
2017-03-04 16:23:36 +01:00
self . uniqueSuffix = str ( uuid . uuid4 ( ) ) . replace ( ' - ' , ' ' )
2015-12-16 17:39:16 +01:00
# Use the ext mechanism
2017-04-03 20:35:03 +10:00
name = self . name ( ) . replace ( ' . ' , ' _ ' )
2015-12-16 17:39:16 +01:00
try :
2017-10-22 10:25:09 +02:00
self . module = importlib . import_module (
' processing.algs.grass7.ext. {} ' . format ( name ) )
2015-12-16 17:39:16 +01:00
except ImportError :
self . module = None
2014-04-19 15:53:43 +02:00
2017-09-04 16:31:31 +02:00
def createInstance ( self ) :
return self . __class__ ( self . descriptionFile )
2017-10-22 10:25:09 +02:00
2017-03-29 12:51:59 +10:00
def name ( self ) :
return self . _name
def displayName ( self ) :
return self . _display_name
2018-05-16 19:22:12 +10:00
def shortDescription ( self ) :
return self . _short_description
2017-03-29 12:04:09 +10:00
def group ( self ) :
return self . _group
2017-12-14 11:21:01 +02:00
def groupId ( self ) :
return self . _groupId
2017-03-29 10:42:42 +10:00
def icon ( self ) :
return QgsApplication . getThemeIcon ( " /providerGrass.svg " )
def svgIconPath ( self ) :
return QgsApplication . iconPath ( " providerGrass.svg " )
2016-09-14 06:42:04 +10:00
2018-01-29 12:21:41 +10:00
def flags ( self ) :
# TODO - maybe it's safe to background thread this?
2018-05-30 12:30:24 +10:00
return super ( ) . flags ( ) | QgsProcessingAlgorithm . FlagNoThreading | QgsProcessingAlgorithm . FlagDisplayNameIsLiteral
2018-01-29 12:21:41 +10:00
2017-09-01 17:53:33 +02:00
def tr ( self , string , context = ' ' ) :
if context == ' ' :
context = self . __class__ . __name__
return QCoreApplication . translate ( context , string )
2017-10-22 10:25:09 +02:00
2017-05-15 13:09:41 +10:00
def helpUrl ( self ) :
2017-01-09 15:32:42 +02:00
helpPath = Grass7Utils . grassHelpPath ( )
if helpPath == ' ' :
2017-05-15 13:09:41 +10:00
return None
2017-01-09 15:32:42 +02:00
if os . path . exists ( helpPath ) :
2017-05-15 13:09:41 +10:00
return QUrl . fromLocalFile ( os . path . join ( helpPath , ' {} .html ' . format ( self . grass7Name ) ) ) . toString ( )
2016-03-06 11:48:41 +01:00
else :
2017-05-15 13:09:41 +10:00
return helpPath + ' {} .html ' . format ( self . grass7Name )
2014-04-19 15:53:43 +02:00
2017-09-03 17:42:13 +02:00
def initAlgorithm ( self , config = None ) :
2017-09-06 14:15:24 +02:00
"""
Algorithm initialization
"""
2017-09-03 17:42:13 +02:00
for p in self . params :
2017-09-06 14:15:24 +02:00
# We use createOutput argument for automatic output creation
2018-11-11 01:24:09 +01:00
self . addParameter ( p , True )
2014-04-19 15:53:43 +02:00
def defineCharacteristicsFromFile ( self ) :
2017-09-01 17:53:33 +02:00
"""
Create algorithm parameters and outputs from a text file .
"""
2016-11-07 10:35:15 +10:00
with open ( self . descriptionFile ) as lines :
2017-09-01 17:53:33 +02:00
# First line of the file is the Grass algorithm name
2016-11-07 10:35:15 +10:00
line = lines . readline ( ) . strip ( ' \n ' ) . strip ( )
self . grass7Name = line
2017-09-01 17:53:33 +02:00
# Second line if the algorithm name in Processing
2016-11-07 10:35:15 +10:00
line = lines . readline ( ) . strip ( ' \n ' ) . strip ( )
2018-05-16 19:22:12 +10:00
self . _short_description = line
if " - " not in line :
self . _name = self . grass7Name
else :
self . _name = line [ : line . find ( ' ' ) ] . lower ( )
2019-03-04 21:14:14 +01:00
self . _short_description = QCoreApplication . translate ( " GrassAlgorithm " , line )
2018-05-16 19:22:12 +10:00
self . _display_name = self . _name
2017-09-01 17:53:33 +02:00
# Read the grass group
2016-11-07 10:35:15 +10:00
line = lines . readline ( ) . strip ( ' \n ' ) . strip ( )
2017-03-29 12:04:09 +10:00
self . _group = QCoreApplication . translate ( " GrassAlgorithm " , line )
2017-12-14 11:21:01 +02:00
self . _groupId = self . groupIdRegex . search ( line ) . group ( 0 ) . lower ( )
2016-11-07 10:35:15 +10:00
hasRasterOutput = False
2017-11-05 13:52:26 +01:00
hasRasterInput = False
2016-11-07 10:35:15 +10:00
hasVectorInput = False
2017-09-04 16:31:31 +02:00
vectorOutputs = False
2017-09-01 17:53:33 +02:00
# Then you have parameters/output definition
2016-11-07 10:35:15 +10:00
line = lines . readline ( ) . strip ( ' \n ' ) . strip ( )
while line != ' ' :
try :
line = line . strip ( ' \n ' ) . strip ( )
if line . startswith ( ' Hardcoded ' ) :
self . hardcodedStrings . append ( line [ len ( ' Hardcoded| ' ) : ] )
2019-03-04 21:14:14 +01:00
parameter = getParameterFromString ( line , " GrassAlgorithm " )
2016-11-07 10:35:15 +10:00
if parameter is not None :
2017-09-03 17:42:13 +02:00
self . params . append ( parameter )
2018-04-08 12:23:15 +10:00
if isinstance ( parameter , ( QgsProcessingParameterVectorLayer , QgsProcessingParameterFeatureSource ) ) :
2016-11-07 10:35:15 +10:00
hasVectorInput = True
2017-11-05 13:52:26 +01:00
elif isinstance ( parameter , QgsProcessingParameterRasterLayer ) :
hasRasterInput = True
elif isinstance ( parameter , QgsProcessingParameterMultipleLayers ) :
if parameter . layerType ( ) < 3 or parameter . layerType ( ) == 5 :
hasVectorInput = True
elif parameter . layerType ( ) == 3 :
hasRasterInput = True
2017-09-04 16:31:31 +02:00
elif isinstance ( parameter , QgsProcessingParameterVectorDestination ) :
vectorOutputs = True
elif isinstance ( parameter , QgsProcessingParameterRasterDestination ) :
hasRasterOutput = True
2016-11-07 10:35:15 +10:00
line = lines . readline ( ) . strip ( ' \n ' ) . strip ( )
except Exception as e :
2018-02-05 22:11:34 -04:00
QgsMessageLog . logMessage ( self . tr ( ' Could not open GRASS GIS 7 algorithm: {0} \n {1} ' ) . format ( self . descriptionFile , line ) , self . tr ( ' Processing ' ) , Qgis . Critical )
2016-11-07 10:35:15 +10:00
raise e
2014-04-19 15:53:43 +02:00
2017-09-04 16:31:31 +02:00
param = QgsProcessingParameterExtent (
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
self . GRASS_REGION_EXTENT_PARAMETER ,
2017-09-04 16:31:31 +02:00
self . tr ( ' GRASS GIS 7 region extent ' ) ,
optional = True
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
)
2017-09-04 16:31:31 +02:00
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
2017-10-22 10:25:09 +02:00
2017-11-05 13:52:26 +01:00
if hasRasterOutput or hasRasterInput :
2017-11-05 13:18:24 +01:00
# Add a cellsize parameter
2017-09-04 16:31:31 +02:00
param = QgsProcessingParameterNumber (
2014-04-19 15:53:43 +02:00
self . GRASS_REGION_CELLSIZE_PARAMETER ,
2015-01-16 15:56:05 +02:00
self . tr ( ' GRASS GIS 7 region cellsize (leave 0 for default) ' ) ,
2017-09-01 17:53:33 +02:00
type = QgsProcessingParameterNumber . Double ,
2017-09-04 16:31:31 +02:00
minValue = 0.0 , maxValue = sys . float_info . max + 1 , defaultValue = 0.0
2017-09-01 17:53:33 +02:00
)
2017-09-04 16:31:31 +02:00
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
2017-10-22 10:25:09 +02:00
2017-11-05 13:52:26 +01:00
if hasRasterOutput :
2017-11-05 13:18:24 +01:00
# Add a createopt parameter for format export
param = QgsProcessingParameterString (
self . GRASS_RASTER_FORMAT_OPT ,
self . tr ( ' Output Rasters format options (createopt) ' ) ,
multiLine = True , optional = True
)
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
# Add a metadata parameter for format export
param = QgsProcessingParameterString (
self . GRASS_RASTER_FORMAT_META ,
self . tr ( ' Output Rasters format metadata options (metaopt) ' ) ,
multiLine = True , optional = True
)
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
2014-04-19 15:53:43 +02:00
if hasVectorInput :
2017-09-01 17:53:33 +02:00
param = QgsProcessingParameterNumber ( self . GRASS_SNAP_TOLERANCE_PARAMETER ,
self . tr ( ' v.in.ogr snap tolerance (-1 = no snap) ' ) ,
type = QgsProcessingParameterNumber . Double ,
2017-09-03 17:42:13 +02:00
minValue = - 1.0 , maxValue = sys . float_info . max + 1 ,
defaultValue = - 1.0 )
2017-05-16 16:36:00 +10:00
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
2017-09-04 16:31:31 +02:00
self . params . append ( param )
2017-09-01 17:53:33 +02:00
param = QgsProcessingParameterNumber ( self . GRASS_MIN_AREA_PARAMETER ,
self . tr ( ' v.in.ogr min area ' ) ,
2017-09-03 17:42:13 +02:00
type = QgsProcessingParameterNumber . Double ,
minValue = 0.0 , maxValue = sys . float_info . max + 1 ,
defaultValue = 0.0001 )
2017-05-16 16:36:00 +10:00
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
2017-09-04 16:31:31 +02:00
self . params . append ( param )
2017-10-22 10:25:09 +02:00
2017-09-04 16:31:31 +02:00
if vectorOutputs :
2018-01-27 10:26:28 +02:00
# Add an optional output type
2017-09-01 17:53:33 +02:00
param = QgsProcessingParameterEnum ( self . GRASS_OUTPUT_TYPE_PARAMETER ,
self . tr ( ' v.out.ogr output type ' ) ,
self . OUTPUT_TYPES )
2017-05-16 16:36:00 +10:00
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
2017-09-04 16:31:31 +02:00
self . params . append ( param )
2014-04-19 15:53:43 +02:00
2018-01-27 10:26:28 +02:00
# Add a DSCO parameter for format export
param = QgsProcessingParameterString (
self . GRASS_VECTOR_DSCO ,
self . tr ( ' v.out.ogr output data source options (dsco) ' ) ,
multiLine = True , optional = True
)
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
# Add a LCO parameter for format export
param = QgsProcessingParameterString (
self . GRASS_VECTOR_LCO ,
self . tr ( ' v.out.ogr output layer options (lco) ' ) ,
multiLine = True , optional = True
)
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
2018-11-05 12:42:12 +00:00
# Add a -c flag for export
param = QgsProcessingParameterBoolean (
self . GRASS_VECTOR_EXPORT_NOCAT ,
2019-01-28 10:07:03 +02:00
self . tr ( ' Also export features without category (not labeled). Otherwise only features with category are exported ' ) ,
2019-01-30 10:40:29 +02:00
False
2018-11-05 12:42:12 +00:00
)
param . setFlags ( param . flags ( ) | QgsProcessingParameterDefinition . FlagAdvanced )
self . params . append ( param )
2017-10-22 10:25:09 +02:00
def getDefaultCellSize ( self ) :
2017-09-08 17:47:24 +02:00
"""
Determine a default cell size from all the raster layers .
"""
2017-09-11 18:19:56 +02:00
cellsize = 0.0
2017-10-22 10:25:09 +02:00
layers = [ l for l in self . inputLayers if isinstance ( l , QgsRasterLayer ) ]
for layer in layers :
2017-10-26 16:09:46 +02:00
cellsize = max ( layer . rasterUnitsPerPixelX ( ) , cellsize )
2017-10-22 10:25:09 +02:00
2017-09-11 18:19:56 +02:00
if cellsize == 0.0 :
cellsize = 100.0
2017-10-22 10:25:09 +02:00
2017-09-11 18:19:56 +02:00
return cellsize
2014-04-19 15:53:43 +02:00
2017-09-06 14:15:24 +02:00
def grabDefaultGrassParameters ( self , parameters , context ) :
"""
Imports default GRASS parameters ( EXTENT , etc ) into
object attributes for faster retrieving .
"""
# GRASS region extent
2017-10-22 10:25:09 +02:00
self . region = self . parameterAsExtent ( parameters ,
self . GRASS_REGION_EXTENT_PARAMETER ,
context )
2017-09-06 14:15:24 +02:00
# GRASS cell size
2017-09-11 18:19:56 +02:00
if self . parameterDefinition ( self . GRASS_REGION_CELLSIZE_PARAMETER ) :
self . cellSize = self . parameterAsDouble ( parameters ,
self . GRASS_REGION_CELLSIZE_PARAMETER ,
context )
2017-09-06 14:15:24 +02:00
# GRASS snap tolerance
self . snapTolerance = self . parameterAsDouble ( parameters ,
2017-10-22 10:25:09 +02:00
self . GRASS_SNAP_TOLERANCE_PARAMETER ,
context )
2017-09-06 14:15:24 +02:00
# GRASS min area
self . minArea = self . parameterAsDouble ( parameters ,
self . GRASS_MIN_AREA_PARAMETER ,
context )
# GRASS output type
self . outputType = self . parameterAsString ( parameters ,
self . GRASS_OUTPUT_TYPE_PARAMETER ,
context )
# GRASS align to resolution
2019-04-16 08:30:00 +02:00
self . alignToResolution = self . parameterAsBoolean ( parameters ,
self . GRASS_REGION_ALIGN_TO_RESOLUTION ,
context )
2017-10-22 10:25:09 +02:00
2018-07-25 08:17:03 +10:00
def processAlgorithm ( self , original_parameters , context , feedback ) :
2017-09-11 18:19:56 +02:00
if isWindows ( ) :
2014-04-19 15:53:43 +02:00
path = Grass7Utils . grassPath ( )
if path == ' ' :
2017-09-01 17:53:33 +02:00
raise QgsProcessingException (
2015-01-16 15:56:05 +02:00
self . tr ( ' GRASS GIS 7 folder is not configured. Please '
' configure it before running GRASS GIS 7 algorithms. ' ) )
2016-01-02 16:14:41 +01:00
2018-07-25 08:17:03 +10:00
# make a copy of the original parameters dictionary - it gets modified by grass algorithms
parameters = { k : v for k , v in original_parameters . items ( ) }
2015-12-16 17:39:16 +01:00
# Create brand new commands lists
self . commands = [ ]
self . outputCommands = [ ]
2014-04-19 15:53:43 +02:00
self . exportedLayers = { }
# If GRASS session has been created outside of this algorithm then
# get the list of layers loaded in GRASS otherwise start a new
# session
existingSession = Grass7Utils . sessionRunning
if existingSession :
self . exportedLayers = Grass7Utils . getSessionLayers ( )
else :
2017-10-26 16:09:46 +02:00
Grass7Utils . startGrassSession ( )
2014-04-19 15:53:43 +02:00
2017-09-06 14:15:24 +02:00
# Handle default GRASS parameters
self . grabDefaultGrassParameters ( parameters , context )
2017-10-22 10:25:09 +02:00
2015-12-16 17:39:16 +01:00
# Handle ext functions for inputs/command/outputs
2017-09-12 21:58:12 +02:00
for fName in [ ' Inputs ' , ' Command ' , ' Outputs ' ] :
fullName = ' process {} ' . format ( fName )
if self . module and hasattr ( self . module , fullName ) :
2018-04-08 09:25:27 +10:00
getattr ( self . module , fullName ) ( self , parameters , context , feedback )
2015-12-16 17:39:16 +01:00
else :
2018-04-08 09:25:27 +10:00
getattr ( self , fullName ) ( parameters , context , feedback )
2015-12-16 17:39:16 +01:00
# Run GRASS
loglines = [ ]
2016-01-14 12:19:31 +01:00
loglines . append ( self . tr ( ' GRASS GIS 7 execution commands ' ) )
2015-12-16 17:39:16 +01:00
for line in self . commands :
2017-01-06 20:04:00 +10:00
feedback . pushCommandInfo ( line )
2015-12-16 17:39:16 +01:00
loglines . append ( line )
if ProcessingConfig . getSetting ( Grass7Utils . GRASS_LOG_COMMANDS ) :
2018-02-05 22:11:34 -04:00
QgsMessageLog . logMessage ( " \n " . join ( loglines ) , self . tr ( ' Processing ' ) , Qgis . Info )
2015-12-16 17:39:16 +01:00
2017-10-26 16:09:46 +02:00
Grass7Utils . executeGrass ( self . commands , feedback , self . outputCommands )
2015-12-16 17:39:16 +01:00
# If the session has been created outside of this algorithm, add
# the new GRASS GIS 7 layers to it otherwise finish the session
if existingSession :
Grass7Utils . addSessionLayers ( self . exportedLayers )
else :
2017-10-26 16:09:46 +02:00
Grass7Utils . endGrassSession ( )
2014-04-19 15:53:43 +02:00
2017-09-06 14:15:24 +02:00
# Return outputs map
outputs = { }
2017-10-22 10:25:09 +02:00
for out in self . outputDefinitions ( ) :
outName = out . name ( )
2017-09-06 14:15:24 +02:00
if outName in parameters :
outputs [ outName ] = parameters [ outName ]
2017-10-22 10:25:09 +02:00
if isinstance ( out , QgsProcessingOutputHtml ) :
self . convertToHtml ( parameters [ outName ] )
2017-09-06 14:15:24 +02:00
return outputs
2018-04-08 09:25:27 +10:00
def processInputs ( self , parameters , context , feedback ) :
2015-12-16 17:39:16 +01:00
""" Prepare the GRASS import commands """
2017-09-06 14:15:24 +02:00
inputs = [ p for p in self . parameterDefinitions ( )
if isinstance ( p , ( QgsProcessingParameterVectorLayer ,
2018-04-08 12:23:15 +10:00
QgsProcessingParameterFeatureSource ,
2017-09-06 14:15:24 +02:00
QgsProcessingParameterRasterLayer ,
QgsProcessingParameterMultipleLayers ) ) ]
for param in inputs :
2017-09-04 16:31:31 +02:00
paramName = param . name ( )
2018-11-11 01:24:09 +01:00
if paramName not in parameters :
2017-10-22 10:25:09 +02:00
continue
2017-12-26 18:53:30 +01:00
# Handle Null parameter
if parameters [ paramName ] is None :
2017-10-22 10:25:09 +02:00
continue
2017-12-26 18:53:30 +01:00
elif isinstance ( parameters [ paramName ] , str ) and len ( parameters [ paramName ] ) == 0 :
continue
2017-12-30 17:16:16 +01:00
2017-09-06 14:15:24 +02:00
# Raster inputs needs to be imported into temp GRASS DB
2017-09-04 16:31:31 +02:00
if isinstance ( param , QgsProcessingParameterRasterLayer ) :
2017-10-22 10:25:09 +02:00
if paramName not in self . exportedLayers :
self . loadRasterLayerFromParameter (
paramName , parameters , context )
2017-09-06 14:15:24 +02:00
# Vector inputs needs to be imported into temp GRASS DB
2018-04-08 12:23:15 +10:00
elif isinstance ( param , ( QgsProcessingParameterFeatureSource , QgsProcessingParameterVectorLayer ) ) :
2017-10-22 10:25:09 +02:00
if paramName not in self . exportedLayers :
2017-12-30 17:16:16 +01:00
# Attribute tables are also vector inputs
if QgsProcessing . TypeFile in param . dataTypes ( ) :
self . loadAttributeTableFromParameter (
paramName , parameters , context )
else :
self . loadVectorLayerFromParameter (
2018-11-01 07:27:39 +10:00
paramName , parameters , context , external = None , feedback = feedback )
2017-10-22 10:25:09 +02:00
# For multiple inputs, process each layer
2017-09-07 18:47:37 +02:00
elif isinstance ( param , QgsProcessingParameterMultipleLayers ) :
layers = self . parameterAsLayerList ( parameters , paramName , context )
for idx , layer in enumerate ( layers ) :
layerName = ' {} _ {} ' . format ( paramName , idx )
2017-10-22 10:25:09 +02:00
# Add a raster layer
2019-03-26 14:08:37 -05:00
if layer . type ( ) == QgsMapLayerType . RasterLayer :
2017-11-04 18:34:25 +01:00
self . loadRasterLayer ( layerName , layer )
2017-10-22 10:25:09 +02:00
# Add a vector layer
2019-03-26 14:08:37 -05:00
elif layer . type ( ) == QgsMapLayerType . VectorLayer :
2018-11-01 07:27:39 +10:00
self . loadVectorLayer ( layerName , layer , external = None , feedback = feedback )
2017-10-22 10:25:09 +02:00
2019-04-02 16:20:08 +10:00
self . postInputs ( context )
2017-10-22 10:25:09 +02:00
2019-04-02 16:20:08 +10:00
def postInputs ( self , context ) :
2017-09-12 21:58:12 +02:00
"""
After layer imports , we need to update some internal parameters
"""
2017-09-04 16:31:31 +02:00
# If projection has not already be set, use the project
self . setSessionProjectionFromProject ( )
2014-04-19 15:53:43 +02:00
2017-09-04 16:31:31 +02:00
# Build GRASS region
2017-09-07 11:25:02 +02:00
if self . region . isEmpty ( ) :
2019-04-12 12:46:03 +02:00
self . region = QgsProcessingUtils . combineLayerExtents ( self . inputLayers , self . destination_crs , context )
2017-09-04 16:31:31 +02:00
command = ' g.region n= {} s= {} e= {} w= {} ' . format (
2017-09-06 14:15:24 +02:00
self . region . yMaximum ( ) , self . region . yMinimum ( ) ,
2017-09-07 11:25:02 +02:00
self . region . xMaximum ( ) , self . region . xMinimum ( )
2017-09-04 16:31:31 +02:00
)
2017-09-08 17:47:24 +02:00
# Handle cell size
2017-09-11 18:19:56 +02:00
if self . parameterDefinition ( self . GRASS_REGION_CELLSIZE_PARAMETER ) :
if self . cellSize :
cellSize = self . cellSize
else :
2017-10-22 10:25:09 +02:00
cellSize = self . getDefaultCellSize ( )
2017-09-11 18:19:56 +02:00
command + = ' res= {} ' . format ( cellSize )
2017-10-22 10:25:09 +02:00
2017-09-08 17:47:24 +02:00
# Handle align to resolution
2017-09-06 14:15:24 +02:00
if self . alignToResolution :
2014-04-19 15:53:43 +02:00
command + = ' -a '
2017-09-06 14:15:24 +02:00
# Add the default parameters commands
2015-12-16 17:39:16 +01:00
self . commands . append ( command )
2017-10-22 10:25:09 +02:00
2018-02-18 15:49:19 +01:00
QgsMessageLog . logMessage ( self . tr ( ' processInputs end. Commands: {} ' ) . format ( self . commands ) , ' Grass7 ' , Qgis . Info )
2014-04-19 15:53:43 +02:00
2018-04-08 09:25:27 +10:00
def processCommand ( self , parameters , context , feedback , delOutputs = False ) :
2017-09-11 18:19:56 +02:00
"""
Prepare the GRASS algorithm command
2017-05-15 16:19:46 +10:00
: param parameters :
2017-10-22 10:25:09 +02:00
: param context :
: param delOutputs : do not add outputs to commands .
2017-05-15 16:19:46 +10:00
"""
2017-09-06 14:15:24 +02:00
noOutputs = [ o for o in self . parameterDefinitions ( ) if o not in self . destinationParameterDefinitions ( ) ]
command = ' {} ' . format ( self . grass7Name )
command + = ' {} ' . join ( self . hardcodedStrings )
2015-10-22 09:44:19 +02:00
2015-12-16 17:39:16 +01:00
# Add algorithm command
2017-09-06 14:15:24 +02:00
for param in noOutputs :
paramName = param . name ( )
value = None
2017-10-22 10:25:09 +02:00
2017-09-06 14:15:24 +02:00
# Exclude default GRASS parameters
if paramName in [ self . GRASS_REGION_CELLSIZE_PARAMETER ,
self . GRASS_REGION_EXTENT_PARAMETER ,
self . GRASS_MIN_AREA_PARAMETER ,
self . GRASS_SNAP_TOLERANCE_PARAMETER ,
self . GRASS_OUTPUT_TYPE_PARAMETER ,
2017-11-05 13:18:24 +01:00
self . GRASS_REGION_ALIGN_TO_RESOLUTION ,
self . GRASS_RASTER_FORMAT_OPT ,
2018-01-27 10:26:28 +02:00
self . GRASS_RASTER_FORMAT_META ,
self . GRASS_VECTOR_DSCO ,
2018-11-05 12:42:12 +00:00
self . GRASS_VECTOR_LCO ,
self . GRASS_VECTOR_EXPORT_NOCAT ] :
2014-04-19 15:53:43 +02:00
continue
2017-10-22 10:25:09 +02:00
2017-09-06 14:15:24 +02:00
# Raster and vector layers
if isinstance ( param , ( QgsProcessingParameterRasterLayer ,
2018-04-08 12:23:15 +10:00
QgsProcessingParameterVectorLayer ,
QgsProcessingParameterFeatureSource ) ) :
2017-09-06 14:15:24 +02:00
if paramName in self . exportedLayers :
value = self . exportedLayers [ paramName ]
2014-04-19 15:53:43 +02:00
else :
2017-09-06 14:15:24 +02:00
value = self . parameterAsCompatibleSourceLayerPath (
parameters , paramName , context ,
QgsVectorFileWriter . supportedFormatExtensions ( )
)
2017-09-07 18:47:37 +02:00
# MultipleLayers
elif isinstance ( param , QgsProcessingParameterMultipleLayers ) :
layers = self . parameterAsLayerList ( parameters , paramName , context )
values = [ ]
for idx in range ( len ( layers ) ) :
layerName = ' {} _ {} ' . format ( paramName , idx )
values . append ( self . exportedLayers [ layerName ] )
value = ' , ' . join ( values )
2017-09-06 14:15:24 +02:00
# For booleans, we just add the parameter name
elif isinstance ( param , QgsProcessingParameterBoolean ) :
2019-04-16 08:30:00 +02:00
if self . parameterAsBoolean ( parameters , paramName , context ) :
2017-09-07 18:47:37 +02:00
command + = ' {} ' . format ( paramName )
2017-12-28 18:28:18 +01:00
# For Extents, remove if the value is null
elif isinstance ( param , QgsProcessingParameterExtent ) :
if self . parameterAsExtent ( parameters , paramName , context ) :
value = self . parameterAsString ( parameters , paramName , context )
2017-09-06 14:15:24 +02:00
# For enumeration, we need to grab the string value
2017-09-01 17:53:33 +02:00
elif isinstance ( param , QgsProcessingParameterEnum ) :
2017-12-27 11:16:14 +01:00
# Handle multiple values
if param . allowMultiple ( ) :
indexes = self . parameterAsEnums ( parameters , paramName , context )
else :
indexes = [ self . parameterAsEnum ( parameters , paramName , context ) ]
2017-12-28 18:28:18 +01:00
if indexes :
value = ' " {} " ' . format ( ' , ' . join ( [ param . options ( ) [ i ] for i in indexes ] ) )
2017-09-06 14:15:24 +02:00
# For strings, we just translate as string
elif isinstance ( param , QgsProcessingParameterString ) :
2017-10-22 10:25:09 +02:00
data = self . parameterAsString ( parameters , paramName , context )
# if string is empty, we don't add it
if len ( data ) > 0 :
value = ' " {} " ' . format (
self . parameterAsString ( parameters , paramName , context )
)
# For fields, we just translate as string
elif isinstance ( param , QgsProcessingParameterField ) :
2017-12-27 11:52:01 +01:00
value = ' , ' . join (
self . parameterAsFields ( parameters , paramName , context )
2017-09-06 14:15:24 +02:00
)
2017-12-27 20:13:05 +01:00
elif isinstance ( param , QgsProcessingParameterFile ) :
2017-12-28 18:28:18 +01:00
if self . parameterAsString ( parameters , paramName , context ) :
value = ' " {} " ' . format (
self . parameterAsString ( parameters , paramName , context )
)
2018-02-26 15:10:24 +10:00
elif isinstance ( param , QgsProcessingParameterPoint ) :
if self . parameterAsString ( parameters , paramName , context ) :
# parameter specified, evaluate as point
# TODO - handle CRS transform
point = self . parameterAsPoint ( parameters , paramName , context )
value = ' {} , {} ' . format ( point . x ( ) , point . y ( ) )
# For numbers, we translate as a string
2017-09-06 14:15:24 +02:00
elif isinstance ( param , ( QgsProcessingParameterNumber ,
QgsProcessingParameterPoint ) ) :
value = self . parameterAsString ( parameters , paramName , context )
# For everything else, we assume that it is a string
else :
value = ' " {} " ' . format (
self . parameterAsString ( parameters , paramName , context )
)
if value :
2018-04-09 12:49:39 +10:00
command + = ' {} = {} ' . format ( paramName . replace ( ' ~ ' , ' ' ) , value )
2017-09-06 14:15:24 +02:00
# Handle outputs
2017-10-22 10:25:09 +02:00
if not delOutputs :
for out in self . destinationParameterDefinitions ( ) :
2017-12-27 12:08:21 +01:00
# We exclude hidden parameters
if out . flags ( ) & QgsProcessingParameterDefinition . FlagHidden :
continue
2017-10-22 10:25:09 +02:00
outName = out . name ( )
# For File destination
if isinstance ( out , QgsProcessingParameterFileDestination ) :
2018-06-01 09:22:46 +10:00
if outName in parameters and parameters [ outName ] is not None :
# for HTML reports, we need to redirect stdout
if out . defaultFileExtension ( ) . lower ( ) == ' html ' :
2019-01-31 15:52:43 +02:00
command + = ' {} =- > " {} " ' . format (
outName ,
self . parameterAsFileOutput ( parameters , outName , context ) )
2018-06-01 09:22:46 +10:00
else :
command + = ' {} = " {} " ' . format (
outName ,
2019-01-31 15:52:43 +02:00
self . parameterAsFileOutput ( parameters , outName , context ) )
2017-10-25 21:29:28 +02:00
# For folders destination
2017-11-04 18:34:25 +01:00
elif isinstance ( out , QgsProcessingParameterFolderDestination ) :
2017-10-25 21:29:28 +02:00
# We need to add a unique temporary basename
uniqueBasename = outName + self . uniqueSuffix
command + = ' {} = {} ' . format ( outName , uniqueBasename )
2017-10-22 10:25:09 +02:00
else :
2018-08-09 10:33:08 +10:00
if outName in parameters and parameters [ outName ] is not None :
# We add an output name to make sure it is unique if the session
# uses this algorithm several times.
uniqueOutputName = outName + self . uniqueSuffix
command + = ' {} = {} ' . format ( outName , uniqueOutputName )
# Add output file to exported layers, to indicate that
# they are present in GRASS
self . exportedLayers [ outName ] = uniqueOutputName
2014-04-19 15:53:43 +02:00
command + = ' --overwrite '
2015-12-16 17:39:16 +01:00
self . commands . append ( command )
2018-02-18 15:49:19 +01:00
QgsMessageLog . logMessage ( self . tr ( ' processCommands end. Commands: {} ' ) . format ( self . commands ) , ' Grass7 ' , Qgis . Info )
2017-10-22 10:25:09 +02:00
def vectorOutputType ( self , parameters , context ) :
""" Determine vector output types for outputs """
self . outType = ' auto '
if self . parameterDefinition ( self . GRASS_OUTPUT_TYPE_PARAMETER ) :
typeidx = self . parameterAsEnum ( parameters ,
self . GRASS_OUTPUT_TYPE_PARAMETER ,
context )
self . outType = ( ' auto ' if typeidx
is None else self . OUTPUT_TYPES [ typeidx ] )
2018-04-08 09:25:27 +10:00
def processOutputs ( self , parameters , context , feedback ) :
2015-12-16 17:39:16 +01:00
""" Prepare the GRASS v.out.ogr commands """
2017-10-22 10:25:09 +02:00
# Determine general vector output type
self . vectorOutputType ( parameters , context )
2017-09-06 14:15:24 +02:00
for out in self . destinationParameterDefinitions ( ) :
outName = out . name ( )
2018-11-11 01:24:09 +01:00
if outName not in parameters :
2018-04-09 14:32:59 +10:00
# skipped output
continue
2017-09-06 14:15:24 +02:00
if isinstance ( out , QgsProcessingParameterRasterDestination ) :
2017-10-22 10:25:09 +02:00
self . exportRasterLayerFromParameter ( outName , parameters , context )
elif isinstance ( out , QgsProcessingParameterVectorDestination ) :
self . exportVectorLayerFromParameter ( outName , parameters , context )
2017-10-25 21:29:28 +02:00
elif isinstance ( out , QgsProcessingParameterFolderDestination ) :
self . exportRasterLayersIntoDirectory ( outName , parameters , context )
2017-10-22 10:25:09 +02:00
def loadRasterLayerFromParameter ( self , name , parameters , context , external = True , band = 1 ) :
"""
2017-10-26 16:09:46 +02:00
Creates a dedicated command to load a raster into
the temporary GRASS DB .
: param name : name of the parameter .
: param parameters : algorithm parameters dict .
: param context : algorithm context .
: param external : True if using r . external .
: param band : imports only specified band . None for all bands .
2017-10-22 10:25:09 +02:00
"""
layer = self . parameterAsRasterLayer ( parameters , name , context )
self . loadRasterLayer ( name , layer , external , band )
2017-12-29 17:13:30 +01:00
def loadRasterLayer ( self , name , layer , external = True , band = 1 , destName = None ) :
2017-09-11 18:19:56 +02:00
"""
Creates a dedicated command to load a raster into
2017-10-26 16:09:46 +02:00
the temporary GRASS DB .
: param name : name of the parameter .
: param layer : QgsMapLayer for the raster layer .
: param external : True if using r . external .
: param band : imports only specified band . None for all bands .
2017-12-29 17:13:30 +01:00
: param destName : force the destination name of the raster .
2017-09-11 18:19:56 +02:00
"""
2017-10-22 10:25:09 +02:00
self . inputLayers . append ( layer )
self . setSessionProjectionFromLayer ( layer )
2017-12-29 17:13:30 +01:00
if not destName :
destName = ' rast_ {} ' . format ( os . path . basename ( getTempFilename ( ) ) )
self . exportedLayers [ name ] = destName
2017-10-22 10:25:09 +02:00
command = ' {0} input= " {1} " {2} output= " {3} " --overwrite -o ' . format (
2017-09-12 21:58:12 +02:00
' r.external ' if external else ' r.in.gdal ' ,
2017-10-26 16:09:46 +02:00
os . path . normpath ( layer . source ( ) ) ,
2017-10-22 10:25:09 +02:00
' band= {} ' . format ( band ) if band else ' ' ,
2017-12-29 17:13:30 +01:00
destName )
2017-10-22 10:25:09 +02:00
self . commands . append ( command )
2017-12-30 17:16:16 +01:00
2017-10-22 10:25:09 +02:00
def exportRasterLayerFromParameter ( self , name , parameters , context , colorTable = True ) :
"""
Creates a dedicated command to export a raster from
temporary GRASS DB into a file via gdal .
2017-10-25 21:29:28 +02:00
: param name : name of the parameter .
2017-10-26 16:09:46 +02:00
: param parameters : Algorithm parameters dict .
2017-10-25 21:29:28 +02:00
: param context : Algorithm context .
2017-10-22 10:25:09 +02:00
: param colorTable : preserve color Table .
"""
2018-04-09 14:32:59 +10:00
fileName = self . parameterAsOutputLayer ( parameters , name , context )
if not fileName :
return
fileName = os . path . normpath ( fileName )
2017-10-22 10:25:09 +02:00
grassName = ' {} {} ' . format ( name , self . uniqueSuffix )
2017-11-05 13:18:24 +01:00
outFormat = Grass7Utils . getRasterFormatFromFilename ( fileName )
createOpt = self . parameterAsString ( parameters , self . GRASS_RASTER_FORMAT_OPT , context )
metaOpt = self . parameterAsString ( parameters , self . GRASS_RASTER_FORMAT_META , context )
self . exportRasterLayer ( grassName , fileName , colorTable , outFormat , createOpt , metaOpt )
def exportRasterLayer ( self , grassName , fileName ,
colorTable = True , outFormat = ' GTiff ' ,
createOpt = None ,
metaOpt = None ) :
2017-10-22 10:25:09 +02:00
"""
Creates a dedicated command to export a raster from
temporary GRASS DB into a file via gdal .
2017-11-05 13:18:24 +01:00
: param grassName : name of the raster to export .
: param fileName : file path of raster layer .
2017-10-22 10:25:09 +02:00
: param colorTable : preserve color Table .
2017-11-05 13:18:24 +01:00
: param outFormat : file format for export .
: param createOpt : creation options for format .
2018-05-30 08:19:19 +10:00
: param metaOpt : metadata options for export .
2017-10-22 10:25:09 +02:00
"""
2017-11-05 13:18:24 +01:00
if not createOpt :
if outFormat in Grass7Utils . GRASS_RASTER_FORMATS_CREATEOPTS :
createOpt = Grass7Utils . GRASS_RASTER_FORMATS_CREATEOPTS [ outFormat ]
2017-10-22 10:25:09 +02:00
for cmd in [ self . commands , self . outputCommands ] :
# Adjust region to layer before exporting
cmd . append ( ' g.region raster= {} ' . format ( grassName ) )
cmd . append (
2017-12-27 20:13:05 +01:00
' r.out.gdal -t -m {0} input= " {1} " output= " {2} " format= " {3} " {4} {5} --overwrite ' . format (
' ' if colorTable else ' -c ' ,
2017-10-22 10:25:09 +02:00
grassName , fileName ,
2017-11-05 13:18:24 +01:00
outFormat ,
' createopt= " {} " ' . format ( createOpt ) if createOpt else ' ' ,
' metaopt= " {} " ' . format ( metaOpt ) if metaOpt else ' '
2017-10-22 10:25:09 +02:00
)
)
2017-12-29 17:13:30 +01:00
def exportRasterLayersIntoDirectory ( self , name , parameters , context , colorTable = True , wholeDB = False ) :
2017-10-25 21:29:28 +02:00
"""
Creates a dedicated loop command to export rasters from
2017-10-26 16:09:46 +02:00
temporary GRASS DB into a directory via gdal .
: param name : name of the output directory parameter .
: param parameters : Algorithm parameters dict .
: param context : Algorithm context .
2017-10-25 21:29:28 +02:00
: param colorTable : preserve color Table .
2017-12-29 17:13:30 +01:00
: param wholeDB : export every raster layer from the GRASSDB
2017-10-25 21:29:28 +02:00
"""
# Grab directory name and temporary basename
2017-10-26 16:09:46 +02:00
outDir = os . path . normpath (
self . parameterAsString ( parameters , name , context ) )
2017-12-29 17:13:30 +01:00
basename = ' '
if not wholeDB :
basename = name + self . uniqueSuffix
2017-10-25 21:29:28 +02:00
# Add a loop export from the basename
for cmd in [ self . commands , self . outputCommands ] :
2017-12-27 20:13:05 +01:00
# TODO Format/options support
2018-11-11 01:24:09 +01:00
if isWindows ( ) :
cmd . append ( " if not exist {0} mkdir {0} " . format ( outDir ) )
cmd . append ( " for /F %% r IN ( ' g.list type^=rast pattern^= \" {0} * \" ' ) do r.out.gdal -m {1} input= %% r output= {2} / %% r.tif {3} " . format (
basename ,
' -t ' if colorTable else ' ' ,
outDir ,
' --overwrite -c createopt= " TFW=YES,COMPRESS=LZW " '
) )
else :
cmd . append ( " for r in $(g.list type=rast pattern= ' {} * ' ); do " . format ( basename ) )
cmd . append ( " r.out.gdal -m {0} input=$ {{ r}} output= {1} /$ {{ r}}.tif {2} " . format (
' -t ' if colorTable else ' ' , outDir ,
' --overwrite -c createopt= " TFW=YES,COMPRESS=LZW " '
) )
cmd . append ( " done " )
2017-10-25 21:29:28 +02:00
2018-04-08 09:25:27 +10:00
def loadVectorLayerFromParameter ( self , name , parameters , context , feedback , external = False ) :
2017-10-22 10:25:09 +02:00
"""
2017-10-26 16:09:46 +02:00
Creates a dedicated command to load a vector into
the temporary GRASS DB .
: param name : name of the parameter
: param parameters : Parameters of the algorithm .
: param context : Processing context
: param external : use v . external ( v . in . ogr if False ) .
2017-10-22 10:25:09 +02:00
"""
layer = self . parameterAsVectorLayer ( parameters , name , context )
2018-11-01 07:56:15 +10:00
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 :
2018-04-08 09:54:18 +10:00
# 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
path = self . parameterAsCompatibleSourceLayerPath ( parameters , name , context ,
QgsVectorFileWriter . supportedFormatExtensions ( ) ,
feedback = feedback )
ogr_layer = QgsVectorLayer ( path , ' ' , ' ogr ' )
2018-11-01 07:27:39 +10:00
self . loadVectorLayer ( name , ogr_layer , external = external , feedback = feedback )
2018-04-08 09:54:18 +10:00
else :
2018-11-01 07:27:39 +10:00
# already an ogr disk based layer source
self . loadVectorLayer ( name , layer , external = external , feedback = feedback )
2017-10-22 10:25:09 +02:00
2018-11-01 07:27:39 +10:00
def loadVectorLayer ( self , name , layer , external = False , feedback = None ) :
2017-10-22 10:25:09 +02:00
"""
Creates a dedicated command to load a vector into
temporary GRASS DB .
: param name : name of the parameter
2017-10-26 16:09:46 +02:00
: param layer : QgsMapLayer for the vector layer .
2017-10-22 10:25:09 +02:00
: param external : use v . external ( v . in . ogr if False ) .
2018-11-01 07:27:39 +10:00
: param feedback : feedback object
2017-10-22 10:25:09 +02:00
"""
# TODO: support multiple input formats
if external is None :
external = ProcessingConfig . getSetting (
Grass7Utils . GRASS_USE_VEXTERNAL )
2018-04-08 09:58:06 +10:00
2018-11-01 07:56:15 +10:00
source_parts = QgsProviderRegistry . instance ( ) . decodeUri ( ' ogr ' , layer . source ( ) )
file_path = source_parts . get ( ' path ' )
layer_name = source_parts . get ( ' layerName ' )
2018-04-08 09:58:06 +10:00
# safety check: we can only use external for ogr layers which support random read
if external :
2018-11-01 07:56:15 +10:00
if feedback is not None :
feedback . pushInfo ( ' Attempting to use v.external for direct layer read ' )
2018-11-01 07:27:39 +10:00
ds = ogr . Open ( file_path )
2018-04-08 09:58:06 +10:00
if ds is not None :
ogr_layer = ds . GetLayer ( )
if ogr_layer is None or not ogr_layer . TestCapability ( ogr . OLCRandomRead ) :
2018-11-01 07:56:15 +10:00
if feedback is not None :
feedback . reportError ( ' Cannot use v.external: layer does not support random read ' )
2018-04-08 09:58:06 +10:00
external = False
else :
2018-11-01 07:56:15 +10:00
if feedback is not None :
feedback . reportError ( ' Cannot use v.external: error reading layer ' )
2018-04-08 09:58:06 +10:00
external = False
2017-10-22 10:25:09 +02:00
self . inputLayers . append ( layer )
self . setSessionProjectionFromLayer ( layer )
2017-12-30 17:16:16 +01:00
destFilename = ' vector_ {} ' . format ( os . path . basename ( getTempFilename ( ) ) )
2017-10-22 10:25:09 +02:00
self . exportedLayers [ name ] = destFilename
2018-11-01 07:56:15 +10:00
command = ' {0} {1} {2} input= " {3} " {4} output= " {5} " --overwrite -o ' . format (
2017-10-22 10:25:09 +02:00
' v.external ' if external else ' v.in.ogr ' ,
' min_area= {} ' . format ( self . minArea ) if not external else ' ' ,
' snap= {} ' . format ( self . snapTolerance ) if not external else ' ' ,
2018-11-01 07:56:15 +10:00
os . path . normpath ( file_path ) ,
' layer= " {} " ' . format ( layer_name ) if layer_name else ' ' ,
2017-10-22 10:25:09 +02:00
destFilename )
self . commands . append ( command )
2018-01-27 10:26:28 +02:00
def exportVectorLayerFromParameter ( self , name , parameters , context , layer = None , nocats = False ) :
2017-10-22 10:25:09 +02:00
"""
2018-01-27 10:26:28 +02:00
Creates a dedicated command to export a vector from
a QgsProcessingParameter .
: param name : name of the parameter .
: param context : parameters context .
: param layer : for vector with multiples layers , exports only one layer .
: param nocats : do not export GRASS categories .
2017-10-22 10:25:09 +02:00
"""
2017-10-26 16:09:46 +02:00
fileName = os . path . normpath (
self . parameterAsOutputLayer ( parameters , name , context ) )
2018-01-27 10:26:28 +02:00
grassName = ' {} {} ' . format ( name , self . uniqueSuffix )
2017-10-22 10:25:09 +02:00
# Find if there is a dataType
dataType = self . outType
if self . outType == ' auto ' :
parameter = self . parameterDefinition ( name )
if parameter :
layerType = parameter . dataType ( )
if layerType in self . QGIS_OUTPUT_TYPES :
dataType = self . QGIS_OUTPUT_TYPES [ layerType ]
2018-05-15 12:53:50 +03:00
outFormat = QgsVectorFileWriter . driverForExtension ( os . path . splitext ( fileName ) [ 1 ] ) . replace ( ' ' , ' _ ' )
2018-01-27 10:26:28 +02:00
dsco = self . parameterAsString ( parameters , self . GRASS_VECTOR_DSCO , context )
lco = self . parameterAsString ( parameters , self . GRASS_VECTOR_LCO , context )
2019-04-16 08:30:00 +02:00
exportnocat = self . parameterAsBoolean ( parameters , self . GRASS_VECTOR_EXPORT_NOCAT , context )
2018-11-05 12:42:12 +00:00
self . exportVectorLayer ( grassName , fileName , layer , nocats , dataType , outFormat , dsco , lco , exportnocat )
2017-10-22 10:25:09 +02:00
2018-01-27 10:26:28 +02:00
def exportVectorLayer ( self , grassName , fileName , layer = None , nocats = False , dataType = ' auto ' ,
2018-11-05 12:42:12 +00:00
outFormat = None , dsco = None , lco = None , exportnocat = False ) :
2017-10-22 10:25:09 +02:00
"""
Creates a dedicated command to export a vector from
2018-01-27 10:26:28 +02:00
temporary GRASS DB into a file via OGR .
: param grassName : name of the vector to export .
: param fileName : file path of vector layer .
: param dataType : export only this type of data .
: param layer : for vector with multiples layers , exports only one layer .
: param nocats : do not export GRASS categories .
: param outFormat : file format for export .
: param dsco : datasource creation options for format .
: param lco : layer creation options for format .
2018-11-05 12:42:12 +00:00
: param exportnocat : do not export features without categories .
2017-10-22 10:25:09 +02:00
"""
2018-06-01 16:53:00 +10:00
if outFormat is None :
outFormat = QgsVectorFileWriter . driverForExtension ( os . path . splitext ( fileName ) [ 1 ] ) . replace ( ' ' , ' _ ' )
2017-10-22 10:25:09 +02:00
for cmd in [ self . commands , self . outputCommands ] :
cmd . append (
2018-11-05 12:42:12 +00:00
' v.out.ogr {0} type= " {1} " input= " {2} " output= " {3} " format= " {4} " {5} {6} {7} {8} --overwrite ' . format (
' ' if nocats else ' ' ,
2018-01-27 10:26:28 +02:00
dataType , grassName , fileName ,
outFormat ,
2017-10-22 19:03:39 +02:00
' layer= {} ' . format ( layer ) if layer else ' ' ,
2018-01-27 10:26:28 +02:00
' dsco= " {} " ' . format ( dsco ) if dsco else ' ' ,
2019-01-28 10:05:34 +02:00
' lco= " {} " ' . format ( lco ) if lco else ' ' ,
' -c ' if exportnocat else ' '
2017-10-22 10:25:09 +02:00
)
)
2014-04-19 15:53:43 +02:00
2017-12-30 17:16:16 +01:00
def loadAttributeTableFromParameter ( self , name , parameters , context ) :
"""
Creates a dedicated command to load an attribute table
into the temporary GRASS DB .
: param name : name of the parameter
: param parameters : Parameters of the algorithm .
: param context : Processing context
"""
table = self . parameterAsVectorLayer ( parameters , name , context )
self . loadAttributeTable ( name , table )
def loadAttributeTable ( self , name , layer , destName = None ) :
"""
Creates a dedicated command to load an attribute table
into the temporary GRASS DB .
: param name : name of the input parameter .
: param layer : a layer object to import from .
: param destName : force the name for the table into GRASS DB .
"""
self . inputLayers . append ( layer )
if not destName :
destName = ' table_ {} ' . format ( os . path . basename ( getTempFilename ( ) ) )
self . exportedLayers [ name ] = destName
command = ' db.in.ogr --overwrite input= " {0} " output= " {1} " ' . format (
os . path . normpath ( layer . source ( ) ) , destName )
self . commands . append ( command )
def exportAttributeTable ( self , grassName , fileName , outFormat = ' CSV ' , layer = 1 ) :
"""
Creates a dedicated command to export an attribute
table from the temporary GRASS DB into a file via ogr .
: param grassName : name of the parameter .
: param fileName : file path of raster layer .
: param outFormat : file format for export .
: param layer : In GRASS a vector can have multiple layers .
"""
for cmd in [ self . commands , self . outputCommands ] :
cmd . append (
' db.out.ogr input= " {0} " output= " {1} " layer= {2} format= {3} --overwrite ' . format (
grassName , fileName , layer , outFormat
)
)
2017-09-04 16:31:31 +02:00
def setSessionProjectionFromProject ( self ) :
"""
Set the projection from the project .
We creates a PROJ4 definition which is transmitted to Grass
"""
2016-05-06 12:02:59 +02:00
if not Grass7Utils . projectionSet and iface :
2019-04-12 12:46:03 +02:00
self . destination_crs = iface . mapCanvas ( ) . mapSettings ( ) . destinationCrs ( )
2016-01-20 16:39:13 +02:00
proj4 = iface . mapCanvas ( ) . mapSettings ( ) . destinationCrs ( ) . toProj4 ( )
2017-09-04 16:31:31 +02:00
command = ' g.proj -c proj4= " {} " ' . format ( proj4 )
2015-12-16 17:39:16 +01:00
self . commands . append ( command )
2014-04-19 15:53:43 +02:00
Grass7Utils . projectionSet = True
2017-09-04 16:31:31 +02:00
def setSessionProjectionFromLayer ( self , layer ) :
"""
Set the projection from a QgsVectorLayer .
We creates a PROJ4 definition which is transmitted to Grass
"""
2014-04-19 15:53:43 +02:00
if not Grass7Utils . projectionSet :
2017-09-04 16:31:31 +02:00
proj4 = str ( layer . crs ( ) . toProj4 ( ) )
2019-04-12 12:46:03 +02:00
self . destination_crs = layer . crs ( )
2017-09-04 16:31:31 +02:00
command = ' g.proj -c proj4= " {} " ' . format ( proj4 )
self . commands . append ( command )
Grass7Utils . projectionSet = True
2014-04-19 15:53:43 +02:00
2017-10-22 10:25:09 +02:00
def convertToHtml ( self , fileName ) :
# Read HTML contents
lines = [ ]
with open ( fileName , ' r ' , encoding = ' utf-8 ' ) as f :
lines = f . readlines ( )
if len ( lines ) > 1 and ' <html> ' not in lines [ 0 ] :
# Then write into the HTML file
with open ( fileName , ' w ' , encoding = ' utf-8 ' ) as f :
f . write ( ' <html><head> ' )
f . write ( ' <meta http-equiv= " Content-Type " content= " text/html; charset=utf-8 " /></head> ' )
f . write ( ' <body><p> ' )
for line in lines :
f . write ( ' {} </br> ' . format ( line ) )
f . write ( ' </p></body></html> ' )
2017-05-15 12:08:26 +10:00
def canExecute ( self ) :
2017-10-26 16:09:46 +02:00
message = Grass7Utils . checkGrassIsInstalled ( )
2017-05-15 12:08:26 +10:00
return not message , message
2014-04-19 15:53:43 +02:00
2017-05-16 15:21:41 +10:00
def checkParameterValues ( self , parameters , context ) :
2018-07-25 08:17:03 +10:00
grass_parameters = { k : v for k , v in parameters . items ( ) }
2015-12-16 17:39:16 +01:00
if self . module :
if hasattr ( self . module , ' checkParameterValuesBeforeExecuting ' ) :
func = getattr ( self . module , ' checkParameterValuesBeforeExecuting ' )
2018-07-25 08:17:03 +10:00
return func ( self , grass_parameters , context )
return super ( ) . checkParameterValues ( grass_parameters , context )