-
Benjamin Jakimow authored
Quick Labels: map canvas menu is now more compact, supports classification values Signed-off-by:
benjamin.jakimow <benjamin.jakimow@geo.hu-berlin.de>
Benjamin Jakimow authoredQuick Labels: map canvas menu is now more compact, supports classification values Signed-off-by:
benjamin.jakimow <benjamin.jakimow@geo.hu-berlin.de>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
temporalprofiles3d.py 15.57 KiB
# -*- coding: utf-8 -*-
"""
***************************************************************************
---------------------
Date : 27.03.2018
Copyright : (C) 2018 by Benjamin Jakimow
Email : benjamin jakimow at geo dot hu-berlin dot de
***************************************************************************
* *
* 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. *
* *
***************************************************************************
"""
# noinspection PyPep8Naming
import sys, os, re, collections, copy
from qgis import *
from qgis.core import *
from qgis.gui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from eotimeseriesviewer.externals.qps.models import Option, OptionListModel
from eotimeseriesviewer.temporalprofiles2d import *
LABEL_EXPRESSION_3D = 'Scaling'
class TemporalProfile3DPlotStyleButton(QPushButton):
sigPlotStyleChanged = pyqtSignal(PlotStyle)
def __init__(self, *args, **kwds):
super(TemporalProfile3DPlotStyleButton, self).__init__(*args, **kwds)
self.mPlotStyle = TemporalProfile3DPlotStyle()
self.mInitialButtonSize = None
self.setStyleSheet('* { padding: 0px; }')
self.clicked.connect(self.showDialog)
self.setPlotStyle(PlotStyle())
def plotStyle(self):
return self.mPlotStyle
def setPlotStyle(self, plotStyle):
if isinstance(plotStyle, TemporalProfile3DPlotStyle):
self.mPlotStyle.copyFrom(plotStyle)
self._updateIcon()
self.sigPlotStyleChanged.emit(self.mPlotStyle)
else:
s = ""
def showDialog(self):
#print(('A',self.mPlotStyle))
style = TemporalProfile3DPlotStyleDialog.getPlotStyle(plotStyle=self.mPlotStyle)
if style:
self.setPlotStyle(style)
s = ""
#print(('B',self.mPlotStyle))
def resizeEvent(self, arg):
self._updateIcon()
def _updateIcon(self):
if self.mInitialButtonSize is None:
self.mInitialButtonSize = self.sizeHint()
self.setIconSize(self.mInitialButtonSize)
if self.mPlotStyle != None:
s = self.mInitialButtonSize
s = self.sizeHint()
#s = QSize()
icon = self.mPlotStyle.createIcon(self.mInitialButtonSize)
self.setIcon(icon)
self.update()
pass
class TemporalProfile3DPlotStyleDialog(QgsDialog):
@staticmethod
def getPlotStyle(*args, **kwds):
"""
Opens a CrosshairDialog.
:param args:
:param kwds:
:return: specified CrosshairStyle if accepted, else None
"""
d = TemporalProfile3DPlotStyleDialog(*args, **kwds)
d.exec_()
if d.result() == QDialog.Accepted:
return d.plotStyle()
else:
return None
def __init__(self, parent=None, plotStyle=None, title='Specify 3D Plot Style'):
super(QgsDialog, self).__init__(parent=parent , buttons=QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.w = TemporalProfilePlotStyle3DWidget(parent=self)
self.setWindowTitle(title)
self.btOk = QPushButton('Ok')
self.btCancel = QPushButton('Cancel')
buttonBar = QHBoxLayout()
#buttonBar.addWidget(self.btCancel)
#buttonBar.addWidget(self.btOk)
l = self.layout()
l.addWidget(self.w)
l.addLayout(buttonBar)
if isinstance(plotStyle, PlotStyle):
self.setPlotStyle(plotStyle)
#self.setLayout(l)
def plotStyle(self):
return self.w.plotStyle()
def setPlotStyle(self, plotStyle):
assert isinstance(plotStyle, PlotStyle)
self.w.setPlotStyle(plotStyle)
class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
ITEM_TYPES = OptionListModel()
ITEM_TYPES.addOption(Option('LinePlotItem', name= '3D Lines'))
ITEM_TYPES.addOption(Option('ScatterPlotItem', name='3D Scatter Plot'))
ITEM_TYPES.addOption(Option('MeshItem', name='3D Mesh'))
sigStyleUpdated = pyqtSignal()
sigUpdated = pyqtSignal()
sigExpressionUpdated = pyqtSignal()
sigSensorChanged = pyqtSignal(SensorInstrument)
def __init__(self, temporalProfile=None):
super(TemporalProfile3DPlotStyle, self).__init__(temporalProfile=temporalProfile)
#assert isinstance(temporalProfile, TemporalProfile)
# get some good defaults
self.setExpression('b')
self.mItemType = 'LinePlotItem'
self.mIsVisible = True
self.m3DItemKWDS = {'color': QColor('green'),
'width': 2.0,
'mode':'line_strip',
'antialias':True}
def setItemKwds(self, kwds):
self.m3DItemKWDS = kwds
#self.updateStyleProperties()
def itemKwds(self):
return self.m3DItemKWDS.copy()
def updateStyleProperties(self):
"""
Updates changes in coloring and visibility
:return:
"""
for pdi in self.mPlotItems:
s = ""
def updateDataProperties(self):
"""
Updates changes in the underlying data or item type
"""
plotDataItems = self.mPlotItems[:]
for pdi in self.mPlotItems:
s = ""
def setItemType(self, itemType):
assert itemType in TemporalProfile3DPlotStyle.ITEM_TYPES.optionValues()
self.mItemType = itemType
self.sigDataUpdated.emit()
#self.updateDataProperties()
def itemType(self):
return self.mItemType
def copyFrom(self, plotStyle):
super(TemporalProfile3DPlotStyle, self).copyFrom(plotStyle)
assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
self.setItemType(plotStyle.itemType())
self.setItemKwds(plotStyle.itemKwds())
def update(self):
for pdi in self.mPlotItems:
assert isinstance(pdi, TemporalProfilePlotDataItem)
pdi.updateStyle()
def createIcon(self, size=None):
if size is None:
size = QSize(100,60)
pm = QPixmap(size)
pm.fill(self.backgroundColor)
p = QPainter(pm)
kwds = self.m3DItemKWDS
text = '3D'
#brush = self.canvas.backgroundBrush()
#c = brush.color()
#c.setAlpha(170)
#brush.setColor(c)
#painter.setBrush(brush)
#painter.setPen(Qt.NoPen)
font = p.font()
fm = QFontMetrics(font)
backGroundSize = QSizeF(fm.size(Qt.TextSingleLine, text))
backGroundSize = QSizeF(backGroundSize.width() + 3, -1 * (backGroundSize.height() + 3))
#backGroundPos = QPointF(ptLabel.x() - 3, ptLabel.y() + 3)
#background = QPolygonF(QRectF(backGroundPos, backGroundSize))
#painter.drawPolygon(background)
color = kwds.get('color')
if color is None:
color = QColor('green')
if self.mItemType == 'LinePlotItem':
text = 'Lines'
elif self.mItemType == 'MeshItem':
text = 'Mesh'
elif self.mItemType == 'ScatterPlot':
text = 'Scatter Plot'
textPen = QPen(Qt.SolidLine)
textPen.setWidth(1)
textPen.setColor(color)
textPos = QPoint(0, int(pm.size().height() * 0.7))
p.setPen(textPen)
p.drawText(textPos, text)
p.end()
icon = QIcon(pm)
return icon
def createPlotItem(self):
"""
Returns the list of PlotItem related to the current settings
:return: [list-of plotitems]
"""
if not OPENGL_AVAILABLE:
return None
import pyqtgraph.opengl as gl
plotItems = []
sensor = self.sensor()
tp = self.temporalProfile()
expression = QgsExpression(self.expression())
if not isinstance(sensor, SensorInstrument) \
or not isinstance(tp, TemporalProfile) \
or not expression.isValid():
return plotItems
feature = QgsFeature()
fields = QgsFields()
field = QgsField('b', QVariant.Double, 'double', 40, 5)
fields.append(field)
feature.setFields(fields)
dataPos = []
x0 = x1 = y0 = y1 = z0 = z1 = 0
for iDate, tsd in enumerate(tp.mTimeSeries):
assert isinstance(tsd, TimeSeriesDate)
if tsd.mSensor != sensor:
continue
data = tp.data(tsd)
bandKeys = sorted([k for k in data.keys() if k.startswith('b') and data[k] != None],
key=lambda k: bandKey2bandIndex(k))
if len(bandKeys) == 0:
continue
t = date2num(tsd.mDate)
x = []
y = []
z = []
for i, k in enumerate(bandKeys):
value = data[k]
feature.setAttribute('b', float(value))
context = QgsExpressionContextUtils.createFeatureBasedContext(feature, feature.fields())
zValue = expression.evaluate(context)
if zValue is not None:
x.append(i)
y.append(t)
z.append(zValue)
else:
s = ""
x = np.asarray(x)
y = np.asarray(y)
z = np.asarray(z)
if iDate == 0:
x0, x1 = (x.min(), x.max())
y0, y1 = (y.min(), y.max())
z0, z1 = (z.min(), z.max())
else:
x0, x1 = (min(x.min(), x0), max(x.max(), x1))
y0, y1 = (min(y.min(), y0), max(y.max(), y1))
z0, z1 = (min(z.min(), z0), max(z.max(), z1))
dataPos.append((x, y, z))
xyz = [(x0, x1), (y0, y1), (z0, z1)]
l = len(dataPos)
if self.mItemType == 'LinePlotItem':
for iPos, pos in enumerate(dataPos):
x, y, z = pos
arr = np.asarray((x, y, z), dtype=np.float64).transpose()
# for i, m in enumerate(xyz):
# m0, m1 = m
# arr[:, i] = (arr[:, i] - m0) / (m1 - m0)
# degug pyqtgraph
kwds = copy.copy(self.m3DItemKWDS)
for k, v in list(kwds.items()):
if isinstance(v, QColor):
kwds[k] = fn.glColor(v)
plt = gl.GLLinePlotItem(pos=arr, **kwds)
plotItems.append(plt)
else:
raise NotImplementedError(self.mItemType)
self.mPlotItems.append(plotItems)
return plotItems
class TemporalProfilePlotStyle3DWidget(QWidget, loadUI('plotstyle3Dwidget.ui')):
sigPlotStyleChanged = pyqtSignal(PlotStyle)
def __init__(self, title='<#>', parent=None):
super(TemporalProfilePlotStyle3DWidget, self).__init__(parent)
self.setupUi(self)
self.mBlockUpdates = False
self.cb3DItemType.setModel(TemporalProfile3DPlotStyle.ITEM_TYPES)
#connect signals
#color buttons
self.btn3DLinePlotItemColor.colorChanged.connect(self.refreshPreview)
self.btn3DScatterPlotItemColor.colorChanged.connect(self.refreshPreview)
#checkboxes
self.cb3DItemType.currentIndexChanged.connect(self.refreshPreview)
self.cb3DLinePlotItemMode.currentIndexChanged.connect(self.refreshPreview)
#spin boxes
self.sb3DLinePlotItemWidth.valueChanged.connect(self.refreshPreview)
self.sb3DScatterPlotItemSize.valueChanged.connect(self.refreshPreview)
self.setPlotStyle(TemporalProfile3DPlotStyle())
self.refreshPreview()
def refreshPreview(self, *args):
if not self.mBlockUpdates:
#print('DEBUG: REFRESH NOW')
style = self.plotStyle()
#todo: set style to style preview
pi = self.plotDataItem
pi.setSymbol(style.markerSymbol)
pi.setSymbolSize(style.markerSize)
pi.setSymbolBrush(style.markerBrush)
pi.setSymbolPen(style.markerPen)
pi.setPen(style.linePen)
pi.update()
self.plotWidget.update()
self.sigPlotStyleChanged.emit(style)
def setPlotStyle(self, style):
assert isinstance(style, TemporalProfile3DPlotStyle)
#set widget values
self.mLastPlotStyle = style
self.mBlockUpdates = True
from eotimeseriesviewer.models import setCurrentComboBoxValue
itemType = style.mItemType
model = self.cb3DItemType.model()
assert isinstance(model, OptionListModel)
setCurrentComboBoxValue(self.cb3DItemType, itemType)
kwds = style.itemKwds()
def d(k, default):
return kwds[k] if k in kwds.keys() else default
self.cbAntialias.setChecked(d('antialias', True))
DEF_COLOR = QColor('green')
if itemType == 'LinePlotItem':
self.btn3DLinePlotItemColor.setColor(d('color', DEF_COLOR))
self.sb3DLinePlotItemWidth.setValue(d('width', 2.0))
setCurrentComboBoxValue(self.cb3DLinePlotItemMode, d('mode', 'lines'))
elif itemType == 'ScatterPlotItem':
self.btn3DScatterPlotItemColor.setColor(d('color', DEF_COLOR))
self.sb3DScatterPlotItemSize.setValue(d('size', 2.0))
self.cb3DScatterPlotItemPxMode.setChecked(d('pxMode', True))
elif itemType == 'MeshItem':
self.btn3DMeshItemColor.setColor(d('color', DEF_COLOR))
self.btn3DMeshItemEdgeColor.setColor(d('edgeColor', DEF_COLOR))
self.cb3DMeshItemDrawEdges.setChecked(d('drawEdges', False))
self.cb3DMeshItemDrawFaces.setChecked(d('drawFaces', True))
self.cb3DMeshItemSmooth.setChecked(d('smooth', True))
self.cb3DMeshItemNormals.setChecked(d('normals', True))
else:
raise NotImplementedError()
self.refreshPreview()
def plotStyleIcon(self):
icon = QIcon()
#todo: get plot preview as 60x60 icon
return icon
def plotStyle(self):
itemType = self.cb3DItemType.currentData(role=Qt.UserRole).value()
style = TemporalProfile3DPlotStyle()
style.setTemporalProfile(self.mLastPlotStyle.temporalProfile())
style.setItemType(itemType)
kwds = {'antialias':self.cbAntialias.isChecked()}
if itemType == 'LinePlotItem':
kwds['color'] = self.btn3DLinePlotItemColor.color()
kwds['width'] = self.sb3DLinePlotItemWidth.value()
kwds['mode'] = self.cb3DLinePlotItemMode.currentData(role=Qt.DisplayRole)
elif itemType == 'ScatterPlotItem':
kwds['color'] = self.btn3DScatterPlotItemColor.color()
kwds['size'] = self.sb3DScatterPlotItemSize.value()
kwds['pxMode'] = self.cb3DScatterPlotItemPxMode.isChecked()
elif itemType == 'MeshItem':
kwds['color'] = self.btn3DMeshItemColor.color()
kwds['edgeColor'] = self.btn3DMeshItemEdgeColor.color()
kwds['drawEdges'] = self.cb3DMeshItemDrawEdges.isChecked()
kwds['drawFaces'] = self.cb3DMeshItemDrawFaces.isChecked()
kwds['smooth'] = self.cb3DMeshItemSmooth.isChecked()
kwds['normals'] = self.cb3DMeshItemNormals.isChecked()
else:
raise NotImplementedError()
style.setItemKwds(kwds)
return style