Newer
Older
from qgis.core import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *
from osgeo import gdal
from timeseriesviewer.utils import loadUI, qgisInstance
from timeseriesviewer.classification.classificationscheme \
import ClassificationSchemeWidget, ClassificationScheme, ClassInfo, ClassificationSchemeComboBox
from timeseriesviewer.timeseries import TimeSeriesDatum
from timeseriesviewer.layerproperties import *

Benjamin Jakimow
committed
#the QgsProject(s) and QgsMapLayerStore(s) to search for QgsVectorLayers
MAP_LAYER_STORES = [QgsProject.instance()]

Benjamin Jakimow
committed
CONFKEY_CLASSIFICATIONSCHEME = 'classificationScheme'

Benjamin Jakimow
committed
CONFKEY_LABELTYPE = 'labelType'

Benjamin Jakimow
committed
class LabelShortcutType(enum.Enum):

Benjamin Jakimow
committed
"""Enumeration for shortcuts to be derived from a TimeSeriesDatum instance"""

Benjamin Jakimow
committed
Off = 'No labeling (default)'
Date = 'Date-Time'
DOY = 'Day of Year (DOY)'
Year = 'Year'
DecimalYear = 'Decimal year'
Sensor = 'Sensor name'

Benjamin Jakimow
committed
Classification = 'Classificaton'
def shortcuts(field:QgsField):
"""
Returns the possible LabelShortCutTypes for a certain field
:param fieldName: str
:return: [list]
"""
assert isinstance(field, QgsField)

Benjamin Jakimow
committed
shortCutsString = [LabelShortcutType.Sensor, LabelShortcutType.Date, LabelShortcutType.Classification]
shortCutsInt = [LabelShortcutType.Year, LabelShortcutType.DOY, LabelShortcutType.Classification]
shortCutsFloat = [LabelShortcutType.Year, LabelShortcutType.DOY, LabelShortcutType.DecimalYear, LabelShortcutType.Classification]

Benjamin Jakimow
committed
options = [LabelShortcutType.Off]
t = field.typeName().lower()
if t == 'string':
options.extend(shortCutsString)
options.extend(shortCutsInt)
options.extend(shortCutsFloat)
elif t.startswith('integer'):
options.extend(shortCutsInt)
elif t.startswith('real'):
options.extend(shortCutsInt)
options.extend(shortCutsFloat)
else:
s = ""

Benjamin Jakimow
committed
result = []
for o in options:
if o not in result:
result.append(o)
return result
def layerClassSchemes(layer:QgsVectorLayer)->list:
assert isinstance(layer, QgsVectorLayer)
schemes = []
for i in range(layer.fields().count()):
setup = layer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
schemes.append(layer)

Benjamin Jakimow
committed

Benjamin Jakimow
committed
def labelShortcutLayerClassificationSchemes(layer:QgsVectorLayer):
"""
Returns the ClassificationSchemes used for labeling shortcuts
:param layer: QgsVectorLayer
:return: [list-of-classificationSchemes]
"""
classSchemes = []
assert isinstance(layer, QgsVectorLayer)
for i in range(layer.fields().count()):
setup = layer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
conf = setup.config()
ci = conf.get(CONFKEY_CLASSIFICATIONSCHEME)
if isinstance(ci, ClassificationScheme) and ci not in classSchemes:
classSchemes.add(ci)
return classSchemes
def labelShortcutLayers()->list:
"""
Returns a list of all known QgsVectorLayer which define at least one LabelShortcutEditWidget
:return: [list-of-QgsVectorLayer]
"""

Benjamin Jakimow
committed
layers = []
classSchemes = set()
for store in MAP_LAYER_STORES:
assert isinstance(store, (QgsProject, QgsMapLayerStore))
for layer in store.mapLayers().values():
if isinstance(layer, QgsVectorLayer):
for i in range(layer.fields().count()):
setup = layer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:

Benjamin Jakimow
committed
if layer not in layers:
layers.append(layer)

Benjamin Jakimow
committed
return layers
def applyShortcutsToRegisteredLayers(tsd:TimeSeriesDatum, classInfos:list):

Benjamin Jakimow
committed
"""
:param tsd:
:param classInfos:
:return:
"""
for layer in labelShortcutLayers():
assert isinstance(layer, QgsVectorLayer)
applyShortcuts(layer, tsd, classInfos)

Benjamin Jakimow
committed
def applyShortcuts(vectorLayer:QgsVectorLayer, tsd:TimeSeriesDatum, classInfos:dict=None):
"""
Labels selected features with information related to TimeSeriesDatum tsd, according to
the settings specified in this model.
:param tsd: TimeSeriesDatum
:param classInfos:
"""
assert isinstance(tsd, TimeSeriesDatum)

Benjamin Jakimow
committed
assert isinstance(classInfos, (dict, None))
assert isinstance(vectorLayer, QgsVectorLayer)
assert vectorLayer.isEditable()

Benjamin Jakimow
committed
#Lookup-Table for classiicationSchemes
LUT_Classifications = dict()
for i in range(vectorLayer.fields().count()):
field = vectorLayer.fields().at(i)
setup = vectorLayer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
conf = setup.config()
labelType = conf.get(CONFKEY_LABELTYPE)
classScheme = conf.get(CONFKEY_CLASSIFICATIONSCHEME)
if labelType == LabelShortcutType.Classification and isinstance(classScheme, ClassificationScheme):
LUT_Classifications[i] = classScheme
LUT_Classifications[classScheme.name()] = classScheme
LUT_Classifications[field.name()] = classScheme
for i in range(vectorLayer.fields().count()):
setup = vectorLayer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
field = vectorLayer.fields().at(i)
assert isinstance(field, QgsField)
conf = setup.config()

Benjamin Jakimow
committed
labelType = conf.get(CONFKEY_LABELTYPE)
if isinstance(labelType, LabelShortcutType):

Benjamin Jakimow
committed
if labelType == LabelShortcutType.Sensor:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.DOY:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.Date:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.DecimalYear:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.Classification:

Benjamin Jakimow
committed
fieldClassScheme = conf.get(CONFKEY_CLASSIFICATIONSCHEME)
if isinstance(fieldClassScheme, ClassificationScheme):
for ciKey, classInfo in classInfos.items():
classScheme = LUT_Classifications.get(ciKey)
if classScheme == fieldClassScheme and classInfo in fieldClassScheme:
if field.type() == QVariant.String:
value = classInfo.name()
else:
value = classInfo.label()
break
if value == None:
continue
if field.type() == QVariant.String:
value = str(value)
for feature in vectorLayer.selectedFeatures():
assert isinstance(feature, QgsFeature)
oldValue = feature.attribute(field.name())
vectorLayer.changeAttributeValue(feature.id(), i, value, oldValue)
class LabelAttributeTableModel(QAbstractTableModel):
def __init__(self, parent=None, *args):
super(LabelAttributeTableModel, self).__init__()
self.cnField = 'Field'
self.cnFieldType = 'Type'

Benjamin Jakimow
committed
self.cnLabel = 'Label shortcut'
self.mColumnNames = [self.cnField, self.cnFieldType, self.cnLabel]

Benjamin Jakimow
committed
#self.mLabelTypes = dict()
self.mVectorLayer = None
def setVectorLayer(self, layer:QgsVectorLayer):
if isinstance(layer, QgsVectorLayer):
layer.attributeAdded.connect(self.resetModel)
layer.attributeDeleted.connect(self.resetModel)
self.mVectorLayer = layer
else:
self.mVectorLayer = None

Benjamin Jakimow
committed
self.resetModel()

Benjamin Jakimow
committed

Benjamin Jakimow
committed
"""
Returns true if a QgsVectorLayer is specified.
:return: bool
"""
return isinstance(self.mVectorLayer, QgsVectorLayer)
def resetModel(self):
self.beginResetModel()

Benjamin Jakimow
committed
if isinstance(self.mVectorLayer, QgsVectorLayer):
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
for i in range(fields.count()):
field = fields.at(i)
assert isinstance(field, QgsField)

Benjamin Jakimow
committed
#self.mLabelTypes[field.name()] = LabelShortcutType.Off
self.endResetModel()
def rowCount(self, parent = QModelIndex())->int:
if isinstance(self.mVectorLayer, QgsVectorLayer):
return self.mVectorLayer.fields().count()
else:
return 0

Benjamin Jakimow
committed
def fieldName2Index(self, fieldName:str)->str:
assert isinstance(fieldName, str)
if isinstance(self.mVectorLayer, QgsVectorLayer):
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)

Benjamin Jakimow
committed
i = fields.indexOf(fieldName)
return self.createIndex(i, 0)
else:
return QModelIndex()

Benjamin Jakimow
committed
def field2index(self, field:QgsField)->QModelIndex:
assert isinstance(field, QgsField)
return self.fieldName2Index(field.name())

Benjamin Jakimow
committed
def index2editorSetup(self, index:QModelIndex):
if index.isValid() and isinstance(self.mVectorLayer, QgsVectorLayer):
return self.mVectorLayer.editorWidgetSetup(index.row())
else:
return None
def index2field(self, index:QModelIndex)->QgsField:
if index.isValid() and isinstance(self.mVectorLayer, QgsVectorLayer):
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
return fields.at(index.row())
else:
return None
def columnCount(self, parent = QModelIndex())->int:
return len(self.mColumnNames)

Benjamin Jakimow
committed

Benjamin Jakimow
committed
def setFieldShortCut(self, fieldName:str, attributeType:LabelShortcutType):

Benjamin Jakimow
committed
if isinstance(fieldName, QgsField):
fieldName = fieldName.name()
assert isinstance(fieldName, str)

Benjamin Jakimow
committed
assert isinstance(attributeType, LabelShortcutType)

Benjamin Jakimow
committed
if self.hasVectorLayer():
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
i = self.mVectorLayer.fields().indexFromName(fieldName)
assert i >= 0
field = self.mVectorLayer.fields().at(i)
idx = self.field2index(field)
self.setData(self.createIndex(idx.row(), 2), attributeType, role=Qt.EditRole)

Benjamin Jakimow
committed
def shortcuts(self, field:QgsField):
"""
Returns the possible LabelShortCutTypes for a certain field
:param fieldName: str
:return: [list]
"""
if not self.hasVectorLayer():
return []
if isinstance(field, QModelIndex):
field = self.index2field(field)
if isinstance(field, str):
i = self.mVectorLayer.fields().lookupField(field)
field = self.mVectorLayer.fields().at(i)
assert isinstance(field, QgsField)
return shortcuts(field)

Benjamin Jakimow
committed
def data(self, index, role = Qt.DisplayRole):
if role is None or not index.isValid():
return None
value = None
columnName = self.mColumnNames[index.column()]

Benjamin Jakimow
committed
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
field = fields.at(index.row())

Benjamin Jakimow
committed
setup = self.mVectorLayer.editorWidgetSetup(index.row())
assert isinstance(setup, QgsEditorWidgetSetup)
if role == Qt.DisplayRole or role == Qt.ToolTipRole:
if columnName == self.cnField:
value = field.name()
elif columnName == self.cnFieldType:
value = '{}'.format(field.typeName())
elif columnName == self.cnLabel:

Benjamin Jakimow
committed
fac = QgsGui.editorWidgetRegistry().factory(setup.type())
value = fac.name()

Benjamin Jakimow
committed
value = setup
return value
def setData(self, index, value, role=None):
if role is None or not index.isValid():
return None
columnName = self.mColumnNames[index.column()]

Benjamin Jakimow
committed
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
field = fields.at(index.row())

Benjamin Jakimow
committed
setup = self.mVectorLayer.editorWidgetSetup(index.row())
assert isinstance(setup, QgsEditorWidgetSetup)
changed = False
if columnName == self.cnLabel and role == Qt.EditRole:

Benjamin Jakimow
committed
if isinstance(value, str):
setup = QgsEditorWidgetSetup(value,{})
self.mVectorLayer.setEditorWidgetSetup(index.row(), setup)
self.dataChanged.emit(index, index, [role])
def columnName(self, index: int)->str:
if isinstance(index, QModelIndex):
if not index.isValid():
return None
index = index.column()
return self.mColumnNames[index]
def flags(self, index):
if index.isValid():
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if self.columnName(index) == self.cnLabel:
flags = flags | Qt.ItemIsEditable
return flags
return None
def headerData(self, col, orientation, role):
if Qt is None:
return None
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.mColumnNames[col]
elif orientation == Qt.Vertical and role == Qt.DisplayRole:
return col
return None
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
class LabelAttributeTypeWidgetDelegate(QStyledItemDelegate):
"""
"""
def __init__(self, tableView: QTableView, labelAttributeTableModel: LabelAttributeTableModel, parent=None):
super(LabelAttributeTypeWidgetDelegate, self).__init__(parent=parent)
assert isinstance(tableView, QTableView)
assert isinstance(labelAttributeTableModel, LabelAttributeTableModel)
self.mTableView = tableView
self.mLabelAttributeTableModel = labelAttributeTableModel
self.setItemDelegates(tableView)
def model(self)->LabelAttributeTableModel:
return self.mTableView.model()
def setItemDelegates(self, tableView):
assert isinstance(tableView, QTableView)
model = self.model()
for c in [model.cnLabel]:
i = model.mColumnNames.index(c)
tableView.setItemDelegateForColumn(i, self)
def columnName(self, index:QModelIndex)->str:
if not index.isValid():
return None
return self.model().mColumnNames[index.column()]
def createEditor(self, parent, option, index):
cname = self.columnName(index)

Benjamin Jakimow
committed
model = self.model()
layer = model.mVectorLayer
idx = index.row()
w = None
if index.isValid() and isinstance(model, LabelAttributeTableModel):
if cname == model.cnLabel:
w = QComboBox(parent=parent)

Benjamin Jakimow
committed
reg = QgsGui.editorWidgetRegistry()
assert isinstance(reg, QgsEditorWidgetRegistry)
factories = reg.factories()
i = 0
for key, fac in reg.factories().items():
score = fac.fieldScore(layer, idx)
if score > 0:
w.addItem(fac.name(), key)
return w
def setEditorData(self, editor, index):
cname = self.columnName(index)

Benjamin Jakimow
committed
model = self.model()
layer = model.mVectorLayer
w = None
if index.isValid() and isinstance(model, LabelAttributeTableModel):
if cname == model.cnLabel and isinstance(editor, QComboBox):

Benjamin Jakimow
committed
key = model.data(index, role=Qt.UserRole)
i = -1
for i in range(editor.count()):

Benjamin Jakimow
committed
if editor.itemData(i, role=Qt.UserRole) == key:
editor.setCurrentIndex(i)
break

Benjamin Jakimow
committed
def setModelData(self, w, model, index):

Benjamin Jakimow
committed
assert isinstance(model, LabelAttributeTableModel)
assert isinstance(index, QModelIndex)
cname = model.columnName(index)
if index.isValid() and isinstance(model, LabelAttributeTableModel):
if cname == model.cnLabel and isinstance(w, QComboBox):

Benjamin Jakimow
committed
model.setData(index, w.currentData(Qt.UserRole), Qt.EditRole)
class LabelingDock(QgsDockWidget, loadUI('labelingdock.ui')):

benjamin.jakimow@geo.hu-berlin.de
committed
def __init__(self, parent=None):
super(LabelingDock, self).__init__(parent)

benjamin.jakimow@geo.hu-berlin.de
committed
self.setupUi(self)
assert isinstance(self.mVectorLayerComboBox, QgsMapLayerComboBox)

benjamin.jakimow@geo.hu-berlin.de
committed
#self.mLabelAttributeModel = LabelAttributeTableModel()
self.mVectorLayerComboBox.setAllowEmptyLayer(True)
allowed = ['DB2', 'WFS', 'arcgisfeatureserver', 'delimitedtext', 'memory', 'mssql', 'ogr', 'oracle', 'ows',
'postgres', 'spatialite', 'virtual']
excluded = [k for k in QgsProviderRegistry.instance().providerList() if k not in allowed]
self.mVectorLayerComboBox.setExcludedProviders(excluded)
self.mVectorLayerComboBox.currentIndexChanged.connect(self.onVectorLayerChanged)
self.mDualView = None
self.mCanvas = QgsMapCanvas(self)
self.mCanvas.setVisible(False)
self.initActions()
self.onVectorLayerChanged()

Benjamin Jakimow
committed
def onSelectedRowChanged(self, current:QModelIndex, previous:QModelIndex):
"""
Sets the
:param current:
:param previous:
:return:
"""

Benjamin Jakimow
committed
toRemove = self.scrollAreaWidgetContents.findChildren(QWidget, options=Qt.FindDirectChildrenOnly)

Benjamin Jakimow
committed
for w in toRemove:
self.scrollAreaWidgetContents.layout().removeWidget(w)

Benjamin Jakimow
committed
w.setParent(None)

Benjamin Jakimow
committed
layer = self.mLayerFieldModel.layer()
if current.isValid() and isinstance(layer, QgsVectorLayer):
i = current.row()
field = layer.fields().at(i)
setup = layer.editorWidgetSetup(i)

Benjamin Jakimow
committed
self.labelFieldName.setText('Field "{}"'.format(field.name()))

Benjamin Jakimow
committed
factoryName = setup.type()
if factoryName == '':
factoryName = QgsGui.editorWidgetRegistry().findBest(layer, layer.fields().at(i).name()).type()
self.mCurrentConfigWidget = QgsGui.editorWidgetRegistry().createConfigWidget(factoryName, layer, i, self.scrollAreaWidgetContents)
if isinstance(self.mCurrentConfigWidget, QgsEditorConfigWidget):
self.mLastConf = w.config()
self.mCurrentConfigWidget.changed.connect(self.onCheckApply)
self.scrollAreaWidgetContents.layout().addWidget(self.mCurrentConfigWidget)
self.onCheckApply()

Benjamin Jakimow
committed
def onCheckApply(self):
"""
Checks if any QgsVectorLayer settings have changed and enables/disable buttons
"""
btnApply = self.buttonBoxEditorWidget.button(QDialogButtonBox.Apply)
btnReset = self.buttonBoxEditorWidget.button(QDialogButtonBox.Reset)
changed = False
for w in self.stackedFieldConfigs.findChildren(FieldConfigEditorWidget):
assert isinstance(w, FieldConfigEditorWidget)
if w.changed():
changed = True
break
btnApply.setEnabled(changed)
btnReset.setEnabled(not changed)

Benjamin Jakimow
committed
def onReset(self):
"""
Reloads the QgsVectorLayer and all its current settings
"""
self.onVectorLayerChanged()

Benjamin Jakimow
committed
def onApply(self):
"""
Stores changes settings to current QgsVectorLayer
"""
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
for w in self.stackedFieldConfigs.findChildren(FieldConfigEditorWidget):
assert isinstance(w, FieldConfigEditorWidget)
if w.changed():
config = w.currentFieldConfig()
lyr.setEditFormConfig(config.index(), config.editorWidgetSetup())
self.onVectorLayerChanged()

Benjamin Jakimow
committed
def onVectorLayerChanged(self):
lyr = self.currentVectorSource()
self.layerFieldConfigEditorWidget.setLayer(lyr)
Loading
Loading full blame...