Skip to content

Commit

Permalink
Merge pull request #5024 from nyalldawson/build-vrt
Browse files Browse the repository at this point in the history
Restore gdal build vrt alg, plus improvements
  • Loading branch information
nyalldawson committed Aug 17, 2017
2 parents fdee6ea + 8139786 commit 6d38894
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 82 deletions.
4 changes: 2 additions & 2 deletions python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py
Expand Up @@ -35,6 +35,7 @@

from .AssignProjection import AssignProjection
from .aspect import aspect
from .buildvrt import buildvrt
from .ColorRelief import ColorRelief
from .tri import tri
from .warp import warp
Expand All @@ -44,7 +45,6 @@
# from .translate import translate
# from .pct2rgb import pct2rgb
# from .merge import merge
# from .buildvrt import buildvrt
# from .polygonize import polygonize
# from .gdaladdo import gdaladdo
# from .ClipByExtent import ClipByExtent
Expand Down Expand Up @@ -144,14 +144,14 @@ def loadAlgorithms(self):
# information(),
AssignProjection(),
aspect(),
buildvrt(),
ColorRelief(),
tri(),
warp(),
# translate(),
# rgb2pct(),
# pct2rgb(),
# merge(),
# buildvrt(),
# polygonize(),
# gdaladdo(),
# ClipByExtent(),
Expand Down
67 changes: 45 additions & 22 deletions python/plugins/processing/algs/gdal/buildvrt.py
Expand Up @@ -29,14 +29,16 @@

from qgis.PyQt.QtGui import QIcon

from qgis.core import QgsProcessingUtils
from qgis.core import (QgsProcessing,
QgsProperty,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterEnum,
QgsProcessingParameterBoolean,
QgsProcessingParameterRasterDestination,
QgsProcessingOutputLayerDefinition,
QgsProcessingUtils)
from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm
from processing.core.outputs import OutputRaster
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterMultipleInput
from processing.core.parameters import ParameterSelection
from processing.algs.gdal.GdalUtils import GdalUtils
from processing.tools import dataobjects

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]

Expand All @@ -55,15 +57,27 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterMultipleInput(self.INPUT,
self.tr('Input layers'), dataobjects.TYPE_RASTER))
self.addParameter(ParameterSelection(self.RESOLUTION,
self.tr('Resolution'), self.RESOLUTION_OPTIONS, 0))
self.addParameter(ParameterBoolean(self.SEPARATE,
self.tr('Layer stack'), True))
self.addParameter(ParameterBoolean(self.PROJ_DIFFERENCE,
self.tr('Allow projection difference'), False))
self.addOutput(OutputRaster(buildvrt.OUTPUT, self.tr('Virtual')))

class ParameterVrtDestination(QgsProcessingParameterRasterDestination):

def __init__(self, name, description):
super().__init__(name, description)

def type(self):
return 'vrt_destination'

def defaultFileExtension(self):
return 'vrt'

self.addParameter(QgsProcessingParameterMultipleLayers(self.INPUT,
self.tr('Input layers'), QgsProcessing.TypeRaster))
self.addParameter(QgsProcessingParameterEnum(self.RESOLUTION,
self.tr('Resolution'), options=self.RESOLUTION_OPTIONS, defaultValue=0))
self.addParameter(QgsProcessingParameterBoolean(self.SEPARATE,
self.tr('Layer stack'), defaultValue=True))
self.addParameter(QgsProcessingParameterBoolean(self.PROJ_DIFFERENCE,
self.tr('Allow projection difference'), defaultValue=False))
self.addParameter(ParameterVrtDestination(self.OUTPUT, self.tr('Virtual')))

def name(self):
return 'buildvirtualraster'
Expand All @@ -80,25 +94,34 @@ def group(self):
def getConsoleCommands(self, parameters, context, feedback):
arguments = []
arguments.append('-resolution')
arguments.append(self.RESOLUTION_OPTIONS[self.getParameterValue(self.RESOLUTION)])
if self.getParameterValue(buildvrt.SEPARATE):
arguments.append(self.RESOLUTION_OPTIONS[self.parameterAsEnum(parameters, self.RESOLUTION, context)])
if self.parameterAsBool(parameters, buildvrt.SEPARATE, context):
arguments.append('-separate')
if self.getParameterValue(buildvrt.PROJ_DIFFERENCE):
if self.parameterAsBool(parameters, buildvrt.PROJ_DIFFERENCE, context):
arguments.append('-allow_projection_difference')
# Always write input files to a text file in case there are many of them and the
# length of the command will be longer then allowed in command prompt
listFile = os.path.join(QgsProcessingUtils.tempFolder(), 'buildvrtInputFiles.txt')
with open(listFile, 'w') as f:
f.write(self.getParameterValue(buildvrt.INPUT).replace(';', '\n'))
layers = []
for l in self.parameterAsLayerList(parameters, self.INPUT, context):
layers.append(l.source())
f.write('\n'.join(layers))
arguments.append('-input_file_list')
arguments.append(listFile)
out = self.getOutputValue(buildvrt.OUTPUT)

out = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
# Ideally the file extensions should be limited to just .vrt but I'm not sure how
# to do it simply so instead a check is performed.
_, ext = os.path.splitext(out)
if not ext.lower() == '.vrt':
out = out.replace(ext, '.vrt')
self.setOutputValue(self.OUTPUT, out)
out = out[:-len(ext)] + '.vrt'
if isinstance(parameters[self.OUTPUT], QgsProcessingOutputLayerDefinition):
output_def = QgsProcessingOutputLayerDefinition(parameters[self.OUTPUT])
output_def.sink = QgsProperty.fromValue(out)
self.setOutputValue(self.OUTPUT, output_def)
else:
self.setOutputValue(self.OUTPUT, out)
arguments.append(out)

return ['gdalbuildvrt', GdalUtils.escapeAndJoin(arguments)]
89 changes: 77 additions & 12 deletions python/plugins/processing/gui/MultipleInputDialog.py
Expand Up @@ -29,12 +29,17 @@

import os

from qgis.core import QgsSettings
from qgis.core import (QgsSettings,
QgsProcessing,
QgsVectorFileWriter,
QgsProviderRegistry,
QgsProcessingModelChildParameterSource)
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtCore import QByteArray
from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox
from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox, QFileDialog
from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem
from processing.tools import dataobjects

pluginPath = os.path.split(os.path.dirname(__file__))[0]
WIDGET, BASE = uic.loadUiType(
Expand All @@ -43,9 +48,11 @@

class MultipleInputDialog(BASE, WIDGET):

def __init__(self, options, selectedoptions=None):
def __init__(self, options, selectedoptions=None, datatype=None):
super(MultipleInputDialog, self).__init__(None)
self.setupUi(self)
self.datatype = datatype
self.model = None

self.lstLayers.setSelectionMode(QAbstractItemView.NoSelection)

Expand All @@ -68,6 +75,11 @@ def __init__(self, options, selectedoptions=None):
self.btnToggleSelection = QPushButton(self.tr('Toggle selection'))
self.buttonBox.addButton(self.btnToggleSelection,
QDialogButtonBox.ActionRole)
if self.datatype is not None:
btnAddFile = QPushButton(self.tr('Add file(s)…'))
btnAddFile.clicked.connect(self.addFiles)
self.buttonBox.addButton(btnAddFile,
QDialogButtonBox.ActionRole)

self.btnSelectAll.clicked.connect(lambda: self.selectAll(True))
self.btnClearSelection.clicked.connect(lambda: self.selectAll(False))
Expand All @@ -76,22 +88,34 @@ def __init__(self, options, selectedoptions=None):
self.settings = QgsSettings()
self.restoreGeometry(self.settings.value("/Processing/multipleInputDialogGeometry", QByteArray()))

self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.populateList()
self.finished.connect(self.saveWindowGeometry)

def saveWindowGeometry(self):
self.settings.setValue("/Processing/multipleInputDialogGeometry", self.saveGeometry())

def populateList(self):
model = QStandardItemModel()
self.model = QStandardItemModel()
for value, text in self.options:
item = QStandardItem(text)
item.setData(value, Qt.UserRole)
item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked)
item.setCheckable(True)
model.appendRow(item)
self.model.appendRow(item)

self.lstLayers.setModel(model)
# add extra options (e.g. manually added layers)
for t in [o for o in self.selectedoptions if not isinstance(o, int)]:
if isinstance(t, QgsProcessingModelChildParameterSource):
item = QStandardItem(t.staticValue())
else:
item = QStandardItem(t)
item.setData(item.text(), Qt.UserRole)
item.setCheckState(Qt.Checked)
item.setCheckable(True)
self.model.appendRow(item)

self.lstLayers.setModel(self.model)

def accept(self):
self.selectedoptions = []
Expand All @@ -106,15 +130,56 @@ def reject(self):
self.selectedoptions = None
QDialog.reject(self)

def getItemsToModify(self):
items = []
if len(self.lstLayers.selectedIndexes()) > 1:
for i in self.lstLayers.selectedIndexes():
items.append(self.model.itemFromIndex(i))
else:
for i in range(self.model.rowCount()):
items.append(self.model.item(i))
return items

def selectAll(self, value):
model = self.lstLayers.model()
for i in range(model.rowCount()):
item = model.item(i)
for item in self.getItemsToModify():
item.setCheckState(Qt.Checked if value else Qt.Unchecked)

def toggleSelection(self):
model = self.lstLayers.model()
for i in range(model.rowCount()):
item = model.item(i)
for item in self.getItemsToModify():
checked = item.checkState() == Qt.Checked
item.setCheckState(Qt.Unchecked if checked else Qt.Checked)

def getFileFilter(self, datatype):
"""
Returns a suitable file filter pattern for the specified parameter definition
:param param:
:return:
"""
if datatype == QgsProcessing.TypeRaster:
return QgsProviderRegistry.instance().fileRasterFilters()
elif datatype == QgsProcessing.TypeFile:
return self.tr('All files (*.*)')
else:
exts = QgsVectorFileWriter.supportedFormatExtensions()
for i in range(len(exts)):
exts[i] = self.tr('{0} files (*.{1})').format(exts[i].upper(), exts[i].lower())
return self.tr('All files (*.*)') + ';;' + ';;'.join(exts)

def addFiles(self):
filter = self.getFileFilter(self.datatype)

settings = QgsSettings()
path = str(settings.value('/Processing/LastInputPath'))

ret, selected_filter = QFileDialog.getOpenFileNames(self, self.tr('Select file(s)'),
path, filter)
if ret:
files = list(ret)
settings.setValue('/Processing/LastInputPath',
os.path.dirname(str(files[0])))
for filename in files:
item = QStandardItem(filename)
item.setData(filename, Qt.UserRole)
item.setCheckState(Qt.Checked)
item.setCheckable(True)
self.model.appendRow(item)
24 changes: 14 additions & 10 deletions python/plugins/processing/gui/MultipleInputPanel.py
Expand Up @@ -28,9 +28,10 @@

import os

from qgis.core import QgsProcessing
from qgis.PyQt import uic
from qgis.PyQt.QtCore import pyqtSignal

''
from processing.gui.MultipleInputDialog import MultipleInputDialog
from processing.gui.MultipleFileInputDialog import MultipleFileInputDialog

Expand Down Expand Up @@ -63,10 +64,10 @@ def setSelectedItems(self, selected):
self.tr('{0} elements selected').format(len(self.selectedoptions)))

def showSelectionDialog(self):
if self.datatype is None:
dlg = MultipleInputDialog(self.options, self.selectedoptions)
else:
if self.datatype == QgsProcessing.TypeFile:
dlg = MultipleFileInputDialog(self.selectedoptions)
else:
dlg = MultipleInputDialog(self.options, self.selectedoptions, datatype=self.datatype)
dlg.exec_()
if dlg.selectedoptions is not None:
self.selectedoptions = dlg.selectedoptions
Expand All @@ -76,12 +77,15 @@ def showSelectionDialog(self):

def updateForOptions(self, options):
selectedoptions = []
selected = [self.options[i] for i in self.selectedoptions]
selected = [self.options[i] if isinstance(i, int) else i for i in self.selectedoptions]
for sel in selected:
try:
idx = options.index(sel)
selectedoptions.append(idx)
except ValueError:
pass
if isinstance(sel, int):
try:
idx = options.index(sel)
selectedoptions.append(idx)
except ValueError:
pass
else:
selectedoptions.append(sel)
self.options = options
self.setSelectedItems(selectedoptions)

0 comments on commit 6d38894

Please sign in to comment.