Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #3044 from arnaud-morvan/processing_output_vectort…
…able

Processing - Output tables with no geometry through OutputVector
  • Loading branch information
volaya committed May 18, 2016
2 parents a9c1996 + 7847f97 commit fc08535
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 32 deletions.
18 changes: 11 additions & 7 deletions python/plugins/processing/algs/qgis/FieldsMapper.py
Expand Up @@ -30,7 +30,7 @@
from qgis.utils import iface
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterTable
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

Expand All @@ -52,13 +52,15 @@ def __init__(self):
def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Refactor fields')
self.group, self.i18n_group = self.trAlgorithm('Vector table tools')
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'),
[ParameterVector.VECTOR_TYPE_ANY], False))
self.addParameter(ParameterTable(self.INPUT_LAYER,
self.tr('Input layer'),
False))
self.addParameter(ParameterFieldsMapping(self.FIELDS_MAPPING,
self.tr('Fields mapping'), self.INPUT_LAYER))
self.tr('Fields mapping'),
self.INPUT_LAYER))
self.addOutput(OutputVector(self.OUTPUT_LAYER,
self.tr('Refactored')))
self.tr('Refactored'),
base_input=self.INPUT_LAYER))

def processAlgorithm(self, progress):
layer = self.getParameterValue(self.INPUT_LAYER)
Expand Down Expand Up @@ -120,7 +122,9 @@ def processAlgorithm(self, progress):
for current, inFeat in enumerate(features):
rownum = current + 1

outFeat.setGeometry(inFeat.geometry())
geometry = inFeat.geometry()
if geometry is not None:
outFeat.setGeometry(geometry)

attrs = []
for i in xrange(0, len(mapping)):
Expand Down
8 changes: 5 additions & 3 deletions python/plugins/processing/algs/qgis/ui/FieldsMapperDialogs.py
Expand Up @@ -56,7 +56,8 @@ def getWidgetFromParameter(self, param):
else:
items = []
self.dependentItems[param.parent] = items
items.append(param.name)
items.append(param)

parent = self.alg.getParameterFromName(param.parent)
if isinstance(parent, ParameterVector):
layers = dataobjects.getVectorLayers(parent.shapetype)
Expand All @@ -76,16 +77,17 @@ def updateDependentFields(self):
layer = sender.itemData(sender.currentIndex())
children = self.dependentItems[sender.name]
for child in children:
widget = self.valueItems[child]
widget = self.valueItems[child.name]
if isinstance(widget, FieldsMappingPanel):
widget.setLayer(layer)
ParametersPanel.updateDependentFields(self)

def somethingDependsOnThisParameter(self, parent):
for param in self.alg.parameters:
if isinstance(param, ParameterFieldsMapping):
if param.parent == parent.name:
return True
return False
return ParametersPanel.somethingDependsOnThisParameter(self, parent)


class FieldsMapperParametersDialog(AlgorithmDialog):
Expand Down
Expand Up @@ -463,7 +463,7 @@ def on_model_rowsInserted(self, parent, start, end):
self.model.index(end, self.model.columnCount() - 1))

def updateLayerCombo(self):
layers = dataobjects.getVectorLayers()
layers = dataobjects.getTables()
layers.sort(key=lambda lay: lay.name())
for layer in layers:
self.layerCombo.addItem(layer.name(), layer)
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/core/GeoAlgorithm.py
Expand Up @@ -354,7 +354,7 @@ def checkOutputFileExtensions(self):
out.value = out.value + '.' + exts[0]
else:
ext = out.value[idx + 1:]
if ext not in exts:
if ext not in exts + ['dbf']:
out.value = out.value + '.' + exts[0]

def resolveTemporaryOutputs(self):
Expand Down
29 changes: 24 additions & 5 deletions python/plugins/processing/core/outputs.py
Expand Up @@ -250,15 +250,34 @@ class OutputVector(Output):
encoding = None
compatible = None

def getFileFilter(self, alg):
def __init__(self, name='', description='', hidden=False, base_input=None):
Output.__init__(self, name, description, hidden)
self.base_input = base_input
self.base_layer = None

def hasGeometry(self):
if self.base_layer is None:
return True
return dataobjects.canUseVectorLayer(self.base_layer, [-1])

def getSupportedOutputVectorLayerExtensions(self):
exts = dataobjects.getSupportedOutputVectorLayerExtensions()
if not self.hasGeometry():
exts = ['dbf'] + [ext for ext in exts if ext in VectorWriter.nogeometry_extensions]
return exts

def getFileFilter(self, alg):
exts = self.getSupportedOutputVectorLayerExtensions()
for i in range(len(exts)):
exts[i] = self.tr('%s files (*.%s)', 'OutputVector') % (exts[i].upper(), exts[i].lower())
return ';;'.join(exts)

def getDefaultFileExtension(self, alg):
supported = alg.provider.getSupportedOutputVectorLayerExtensions()
default = ProcessingConfig.getSetting(ProcessingConfig.DEFAULT_OUTPUT_VECTOR_LAYER_EXT)
supported = self.getSupportedOutputVectorLayerExtensions()
if self.hasGeometry():
default = ProcessingConfig.getSetting(ProcessingConfig.DEFAULT_OUTPUT_VECTOR_LAYER_EXT)
else:
default = 'dbf'
ext = default if default in supported else supported[0]
return ext

Expand All @@ -271,9 +290,8 @@ def getCompatibleFileName(self, alg):
temporary file with a supported file format, to be used to
generate the output result.
"""

ext = self.value[self.value.rfind('.') + 1:]
if ext in alg.provider.getSupportedOutputVectorLayerExtensions():
if ext in self.getSupportedOutputVectorLayerExtensions():
return self.value
else:
if self.compatible is None:
Expand Down Expand Up @@ -307,4 +325,5 @@ def getVectorWriter(self, fields, geomType, crs, options=None):
w = VectorWriter(self.value, self.encoding, fields, geomType,
crs, options)
self.layer = w.layer
self.value = w.destination
return w
7 changes: 5 additions & 2 deletions python/plugins/processing/gui/OutputSelectionPanel.py
Expand Up @@ -142,7 +142,9 @@ def saveToPostGIS(self):
password = settings.value(mySettings + '/password')
uri = QgsDataSourceURI()
uri.setConnection(host, str(port), dbname, user, password)
uri.setDataSource(dlg.schema, dlg.table, "the_geom")
uri.setDataSource(dlg.schema, dlg.table,
"the_geom" if self.output.hasGeometry() else None)

connInfo = uri.connectionInfo()
(success, user, passwd) = QgsCredentials.instance().get(connInfo, None, None)
if success:
Expand Down Expand Up @@ -182,7 +184,8 @@ def saveToSpatialite(self):

uri = QgsDataSourceURI()
uri.setDatabase(fileName)
uri.setDataSource('', self.output.name.lower(), 'the_geom')
uri.setDataSource('', self.output.name.lower(),
'the_geom' if self.output.hasGeometry() else None)
self.leText.setText("spatialite:" + uri.uri())

def saveToMemory(self):
Expand Down
37 changes: 30 additions & 7 deletions python/plugins/processing/gui/ParametersPanel.py
Expand Up @@ -259,6 +259,22 @@ def initWidgets(self):
self.checkBoxes[output.name] = check
self.valueItems[output.name] = widget

if isinstance(output, OutputVector):
if output.base_input in self.dependentItems:
items = self.dependentItems[output.base_input]
else:
items = []
self.dependentItems[output.base_input] = items
items.append(output)

base_input = self.alg.getParameterFromName(output.base_input)
if isinstance(base_input, ParameterVector):
layers = dataobjects.getVectorLayers(base_input.shapetype)
else:
layers = dataobjects.getTables()
if len(layers) > 0:
output.base_layer = layers[0]

def buttonToggled(self, value):
if value:
sender = self.sender()
Expand Down Expand Up @@ -343,7 +359,7 @@ def getWidgetFromParameter(self, param):
else:
items = []
self.dependentItems[param.parent] = items
items.append(param.name)
items.append(param)
parent = self.alg.getParameterFromName(param.parent)
if isinstance(parent, ParameterVector):
layers = dataobjects.getVectorLayers(parent.shapetype)
Expand Down Expand Up @@ -434,12 +450,15 @@ def updateDependentFields(self):
return
children = self.dependentItems[sender.name]
for child in children:
widget = self.valueItems[child]
widget.clear()
if self.alg.getParameterFromName(child).optional:
widget.addItem(self.tr('[not set]'))
widget.addItems(self.getFields(layer,
self.alg.getParameterFromName(child).datatype))
if isinstance(child, ParameterTableField):
widget = self.valueItems[child.name]
widget.clear()
if self.alg.getParameterFromName(child).optional:
widget.addItem(self.tr('[not set]'))
widget.addItems(self.getFields(layer,
self.alg.getParameterFromName(child).datatype))
if isinstance(child, OutputVector):
child.base_layer = layer

def getFields(self, layer, datatype):
fieldTypes = []
Expand All @@ -460,4 +479,8 @@ def somethingDependsOnThisParameter(self, parent):
if isinstance(param, ParameterTableField):
if param.parent == parent.name:
return True
for output in self.alg.outputs:
if isinstance(output, OutputVector):
if output.base_layer == parent.name:
return True
return False
32 changes: 26 additions & 6 deletions python/plugins/processing/tools/vector.py
Expand Up @@ -47,6 +47,7 @@


GEOM_TYPE_MAP = {
QGis.WKBNoGeometry: 'none',
QGis.WKBPoint: 'Point',
QGis.WKBLineString: 'LineString',
QGis.WKBPolygon: 'Polygon',
Expand Down Expand Up @@ -535,6 +536,13 @@ class VectorWriter:
POSTGIS_LAYER_PREFIX = 'postgis:'
SPATIALITE_LAYER_PREFIX = 'spatialite:'

nogeometry_extensions = [
u'csv',
u'dbf',
u'ods',
u'xlsx',
]

def __init__(self, destination, encoding, fields, geometryType,
crs, options=None):
self.destination = destination
Expand Down Expand Up @@ -592,9 +600,10 @@ def _runSQL(sql):
for f in fields)

_runSQL("CREATE TABLE %s.%s (%s)" % (uri.schema(), uri.table().lower(), fieldsdesc))
_runSQL("SELECT AddGeometryColumn('{schema}', '{table}', 'the_geom', {srid}, '{typmod}', 2)".format(
table=uri.table().lower(), schema=uri.schema(), srid=crs.authid().split(":")[-1],
typmod=GEOM_TYPE_MAP[geometryType].upper()))
if geometryType != QGis.WKBNoGeometry:
_runSQL("SELECT AddGeometryColumn('{schema}', '{table}', 'the_geom', {srid}, '{typmod}', 2)".format(
table=uri.table().lower(), schema=uri.schema(), srid=crs.authid().split(":")[-1],
typmod=GEOM_TYPE_MAP[geometryType].upper()))

self.layer = QgsVectorLayer(uri.uri(), uri.table(), "postgres")
self.writer = self.layer.dataProvider()
Expand Down Expand Up @@ -622,9 +631,10 @@ def _runSQL(sql):

_runSQL("DROP TABLE IF EXISTS %s" % uri.table().lower())
_runSQL("CREATE TABLE %s (%s)" % (uri.table().lower(), fieldsdesc))
_runSQL("SELECT AddGeometryColumn('{table}', 'the_geom', {srid}, '{typmod}', 2)".format(
table=uri.table().lower(), srid=crs.authid().split(":")[-1],
typmod=GEOM_TYPE_MAP[geometryType].upper()))
if geometryType != QGis.WKBNoGeometry:
_runSQL("SELECT AddGeometryColumn('{table}', 'the_geom', {srid}, '{typmod}', 2)".format(
table=uri.table().lower(), srid=crs.authid().split(":")[-1],
typmod=GEOM_TYPE_MAP[geometryType].upper()))

self.layer = QgsVectorLayer(uri.uri(), uri.table(), "spatialite")
self.writer = self.layer.dataProvider()
Expand All @@ -636,12 +646,22 @@ def _runSQL(sql):
extension = extension[extension.find('*.') + 2:]
extension = extension[:extension.find(' ')]
OGRCodes[extension] = value
OGRCodes['dbf'] = "DBF file"

extension = self.destination[self.destination.rfind('.') + 1:]

if extension not in OGRCodes:
extension = 'shp'
self.destination = self.destination + '.shp'

if geometryType == QGis.WKBNoGeometry:
if extension == 'shp':
extension = 'dbf'
self.destination = self.destination[:self.destination.rfind('.')] + '.dbf'
if extension not in self.nogeometry_extensions:
raise GeoAlgorithmExecutionException(
"Unsupported format for tables with no geometry")

qgsfields = QgsFields()
for field in fields:
qgsfields.append(_toQgsField(field))
Expand Down

0 comments on commit fc08535

Please sign in to comment.