Newer
Older
# -*- coding: utf-8 -*-
# noinspection PyPep8Naming
"""
***************************************************************************
cursorlocationvalue.py
---------------------
Date : August 2017
Copyright : (C) 2017 by Benjamin Jakimow
Email : benjamin.jakimow@geo.hu-berlin.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. *
* *
***************************************************************************
"""
import os, collections
import numpy as np
from qgis.core import *
from qgis.gui import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *
def __init__(self, source, point:SpatialPoint):
assert isinstance(point, SpatialPoint)
def baseName(self):
return os.path.basename(self.source)
def crs(self):
return QgsCoordinateReferenceSystem(self.wktCrs)
class RasterValueSet(SourceValueSet):
class BandInfo(object):
def __init__(self, bandIndex, bandValue, bandName):
assert bandIndex >= 0
if bandValue is not None:
assert type(bandValue) in [float, int]
if bandName is not None:

Benjamin Jakimow
committed
assert isinstance(bandName, str)
self.bandIndex = bandIndex
self.bandValue = bandValue
self.bandName = bandName
self.pxPosition = pxPosition
self.noDataValue = None
self.bandValues = []
class VectorValueSet(SourceValueSet):
class FeatureInfo(object):
def __init__(self, fid):
assert isinstance(fid, int)
self.fid = fid
self.attributes = collections.OrderedDict()
def __init__(self, source, point:SpatialPoint):
super(VectorValueSet, self).__init__(source, point)
self.features = []
def addFeatureInfo(self, featureInfo):
assert isinstance(featureInfo, VectorValueSet.FeatureInfo)
self.features.append(featureInfo)
class CursorLocationInfoModel(TreeModel):
ALWAYS_EXPAND = 'always'
NEVER_EXPAND = 'never'
REMAINDER = 'reminder'
def __init__(self, parent=None):
super(CursorLocationInfoModel, self).__init__(parent)
self.mExpandedNodeRemainder = {}
self.mNodeExpansion = CursorLocationInfoModel.REMAINDER
def setNodeExpansion(self, type):
assert type in [CursorLocationInfoModel.ALWAYS_EXPAND,
CursorLocationInfoModel.NEVER_EXPAND,
CursorLocationInfoModel.REMAINDER]
self.mNodeExpansion = type
def setExpandedNodeRemainder(self, node=None):
treeView = self.mTreeView
assert isinstance(treeView, QTreeView)
if node is None:
for n in self.mRootNode.childNodes():
else:
self.mExpandedNodeRemainder[self.weakNodeId(node)] = self.mTreeView.isExpanded(self.node2idx(node))
for n in node.childNodes():
def weakNodeId(self, node):
assert isinstance(node, TreeNode)
n = node.name()
while node.parentNode() != self.mRootNode:
node = node.parentNode()
n += '{}:{}'.format(node.name(), n)
return n
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def addSourceValues(self, sourceValueSet):
if not isinstance(sourceValueSet, SourceValueSet):
return
assert isinstance(root, TreeNode)
n = TreeNode(root, name)
weakId = self.weakNodeId(n)
expand = False
else:
if self.mNodeExpansion == CursorLocationInfoModel.REMAINDER:
expand = self.mExpandedNodeRemainder.get(weakId, False)
elif self.mNodeExpansion == CursorLocationInfoModel.NEVER_EXPAND:
expand = False
elif self.mNodeExpansion == CursorLocationInfoModel.ALWAYS_EXPAND:
expand = True
self.mTreeView.setExpanded(self.node2idx(n), expand)
return n
bn = os.path.basename(sourceValueSet.source)
if isinstance(sourceValueSet, RasterValueSet):
root = gocn(self.mRootNode, name=bn)
n = gocn(root, 'Pixel')
n.setValues('{},{}'.format(sourceValueSet.pxPosition.x(), sourceValueSet.pxPosition.y()))
for bv in sourceValueSet.bandValues:
if isinstance(bv, RasterValueSet.BandInfo):
n = gocn(root, 'Band {}'.format(bv.bandIndex + 1))
n.setToolTip('Band {} {}'.format(bv.bandIndex + 1, bv.bandName).strip())
n.setValues([bv.bandValue, bv.bandName])
elif isinstance(bv, QColor):
n = gocn(root, 'Color')
n.setToolTip('Color selected from screen pixel')
n.setValues(bv.getRgb())
if isinstance(sourceValueSet, VectorValueSet):
if len(sourceValueSet.features) == 0:
return
root = gocn(self.mRootNode, name=bn)
refFeature = sourceValueSet.features[0]
assert isinstance(refFeature, QgsFeature)

Benjamin Jakimow
committed
typeName = QgsWkbTypes.displayString(refFeature.geometry().wkbType()).lower()
if 'polygon' in typeName:
root.setIcon(QIcon(r':/images/themes/default/mIconPolygonLayer.svg'))
elif 'line' in typeName:
root.setIcon(QIcon(r':/images/themes/default/mIconLineLayer.svg'))
if 'point' in typeName:
root.setIcon(QIcon(r':/images/themes/default/mIconPointLayer.svg'))
for field in refFeature.fields():
assert isinstance(field, QgsField)
fieldNode = gocn(root, name=field.name())
for i, feature in enumerate(sourceValueSet.features):
assert isinstance(feature, QgsFeature)
nf = gocn(fieldNode, name='{}'.format(feature.id()))
nf.setValues([feature.attribute(field.name()), field.typeName()])
nf.setToolTip('Value of feature "{}" in field with name "{}"'.format(feature.id(), field.name()))
s = ""
def clear(self):
self.mRootNode.removeChildNodes(0, self.mRootNode.childCount())
class ComboBoxOption(object):
def __init__(self, value, name=None, tooltip=None, icon=None):
self.value = value
self.name = str(value) if name is None else str(name)
self.tooltip = tooltip
self.icon = icon
LUT_GEOMETRY_ICONS = {}
RASTERBANDS = [
ComboBoxOption('VISIBLE', 'RGB', 'Visible bands only.'),
ComboBoxOption('TOP_LAYER', 'Top layer', 'Show values of the top-most map layer only.'),
ComboBoxOption('ALL_LAYERS', 'All layers', 'Show values of all map layers.')
]
LAYERTYPES = [
ComboBoxOption('ALL', 'Raster and Vector', 'Show values of both, raster and vector layers.'),
ComboBoxOption('VECTOR', 'Vector only', 'Show values of vector layers only.'),
ComboBoxOption('RASTER', 'Raster only', 'Show values of raster layers only.')
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
class ComboBoxOptionModel(QAbstractListModel):
def __init__(self, options, parent=None, ):
super(ComboBoxOptionModel, self).__init__(parent)
assert isinstance(options, list)
for o in options:
assert isinstance(o, ComboBoxOption)
self.mOptions = options
def rowCount(self, parent=None, *args, **kwargs):
return len(self.mOptions)
def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
return 1
def index2option(self, index):
if isinstance(index, QModelIndex) and index.isValid():
return self.mOptions[index.row()]
elif isinstance(index, int):
return self.mOptions[index]
return None
def option2index(self, option):
assert option in self.mOptions
return self.mOptions.index(option)
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
option = self.index2option(index)
assert isinstance(option, ComboBoxOption)
value = None
if role == Qt.DisplayRole:
value = option.name
if role == Qt.ToolTipRole:
value = option.tooltip
if role == Qt.DecorationRole:
value = option.icon
if role == Qt.UserRole:
value = option
return value
class CursorLocationInfoDock(QDockWidget,
loadUI('cursorlocationinfodock.ui')):
sigLocationRequest = pyqtSignal()
sigCursorLocationInfoAdded = pyqtSignal()
def __init__(self, parent=None):
"""Constructor."""
QWidget.__init__(self, parent)
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# Set up the user interface from Designer.
# After setupUI you can access any designer object by doing
# self.<objectname>, and you can use autoconnect slots - see
# http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
# #widgets-and-dialogs-with-auto-connect
self.setupUi(self)
self.mMaxPoints = 1
self.mLocationHistory = []
self.mCrs = None
self.mCanvases = []
self.btnCrs.crsChanged.connect(self.setCrs)
self.btnCrs.setCrs(QgsCoordinateReferenceSystem())
self.mLocationInfoModel = CursorLocationInfoModel(parent=self.treeView)
self.treeView.setModel(self.mLocationInfoModel)
self.mLayerModeModel = ComboBoxOptionModel(LAYERMODES, parent=self)
self.mLayerTypeModel = ComboBoxOptionModel(LAYERTYPES, parent=self)
self.mRasterBandsModel = ComboBoxOptionModel(RASTERBANDS, parent=self)
self.cbLayerModes.setModel(self.mLayerModeModel)
self.cbLayerTypes.setModel(self.mLayerTypeModel)
self.cbRasterBands.setModel(self.mRasterBandsModel)
self.actionRequestCursorLocation.triggered.connect(self.sigLocationRequest)
self.actionReload.triggered.connect(self.reloadCursorLocation)
self.btnActivateMapTool.setDefaultAction(self.actionRequestCursorLocation)
self.btnReload.setDefaultAction(self.actionReload)
self.actionAllRasterBands.triggered.connect(
lambda: self.btnRasterBands.setDefaultAction(self.actionAllRasterBands))
self.actionVisibleRasterBands.triggered.connect(
lambda: self.btnRasterBands.setDefaultAction(self.actionVisibleRasterBands))
def options(self):
layerType = self.mLayerTypeModel.index2option(self.cbLayerTypes.currentIndex()).value
layerMode = self.mLayerModeModel.index2option(self.cbLayerModes.currentIndex()).value
rasterBands = self.mRasterBandsModel.index2option(self.cbRasterBands.currentIndex()).value
return (layerMode, layerType, rasterBands)
def loadCursorLocation(self, point:SpatialPoint, canvas:QgsMapCanvas):
"""
:param point:
:param canvas:
:return:
"""
crs = canvas.mapSettings().destinationCrs()
self.setCanvas(canvas)
self.reloadCursorLocation()
def reloadCursorLocation(self):
"""
Call to load / re-load the data for the specifed cursor location
"""
if not isinstance(ptInfo, SpatialPoint) or len(self.mCanvases) == 0:
return
mode, type, rasterbands = self.options()
def layerFilter(canvas):
assert isinstance(canvas, QgsMapCanvas)
lyrs = canvas.layers()
if type == 'VECTOR':
lyrs = [l for l in lyrs if isinstance(l, QgsVectorLayer)]
if type == 'RASTER':
lyrs = [l for l in lyrs if isinstance(l, QgsRasterLayer)]
return lyrs
lyrs = []
for c in self.mCanvases:
lyrs.extend(layerFilter(c))
self.mLocationInfoModel.setExpandedNodeRemainder()
self.mLocationInfoModel.clear()
for l in lyrs:
assert isinstance(l, QgsMapLayer)
if mode == 'TOP_LAYER' and self.mLocationInfoModel.mRootNode.childCount() > 0:
s = ""
break
assert isinstance(l, QgsMapLayer)
pointLyr = ptInfo.toCrs(l.crs())
if not isinstance(pointLyr, SpatialPoint) or not l.extent().contains(pointLyr):
continue
if isinstance(l, QgsRasterLayer):
renderer = l.renderer()
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
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
437
438
439
440
px = geo2px(pointLyr, l)
v = RasterValueSet(l.name(), pointLyr, px)
# !Note: b is not zero-based -> 1st band means b == 1
if rasterbands == 'VISIBLE':
bandNumbers = renderer.usesBands()
elif rasterbands == 'ALL':
bandNumbers = list(range(1, l.bandCount()+1))
else:
bandNumbers = [1]
pt2 = QgsPointXY(pointLyr.x() + l.rasterUnitsPerPixelX() * 3,
pointLyr.y() - l.rasterUnitsPerPixelY() * 3)
ext2Px = QgsRectangle(pointLyr.x(), pt2.y(), pt2.x(), pointLyr.y())
if l.dataProvider().name() in ['wms']:
for b in bandNumbers:
block = l.renderer().block(b, ext2Px, 3, 3)
assert isinstance(block, QgsRasterBlock)
v.bandValues.append(QColor(block.color(0, 0)))
else:
results = l.dataProvider().identify(pointLyr, QgsRaster.IdentifyFormatValue).results()
for b in bandNumbers:
info = RasterValueSet.BandInfo(b - 1, results[b], l.bandName(b))
v.bandValues.append(info)
if False:
results = l.dataProvider().identify(pointLyr, QgsRaster.IdentifyFormatValue).results()
#x, y = pointLyr.x(), pointLyr.y()
#dx = 0.5 * l.rasterUnitsPerPixelX()
#dy = 0.5 * l.rasterUnitsPerPixelY()
#bb = QgsRectangle(x-dx, y-dy, x+dx, y+dy)
#results2 = l.dataProvider().identify(pointLyr, QgsRaster.IdentifyFormatValue, boundingBox=bb, width=1, height=1).results()
if len(results) == 0:
# fallback: get the colors, e.g. in case of WMS
pt2 = QgsPointXY(pointLyr.x() + l.rasterUnitsPerPixelX() * 3,
pointLyr.y() - l.rasterUnitsPerPixelY() * 3)
ext2Px = QgsRectangle(pointLyr.x(), pt2.y(), pt2.x(), pointLyr.y())
block = l.renderer().block(1, ext2Px, 3, 3)
color = QColor(block.color(0, 0))
v.bandValues.append(color)
else:
wl, wlu = parseWavelength(l)
for band, value in results.items():
v.bandValues.append(RasterValueSet.BandInfo(band-1, value, l.bandName(band)))
self.mLocationInfoModel.addSourceValues(v)
s = ""
# searchRadius = QgsTolerance.toleranceInMapUnits(1, l, self.mCanvas.mapRenderer(), QgsTolerance.Pixels)
searchRadius = QgsTolerance.toleranceInMapUnits(1, l, self.mCanvases[0].mapSettings(),
QgsTolerance.Pixels)
# searchRadius = QgsTolerance.defaultTolerance(l, self.mCanvas.mapSettings())
# searchRadius = QgsTolerance.toleranceInProjectUnits(1, self.mCanvas.mapRenderer(), QgsTolerance.Pixels)
searchRect = QgsRectangle()
searchRect.setXMinimum(pointLyr.x() - searchRadius);
searchRect.setXMaximum(pointLyr.x() + searchRadius);
searchRect.setYMinimum(pointLyr.y() - searchRadius);
searchRect.setYMaximum(pointLyr.y() + searchRadius);
flags = QgsFeatureRequest.ExactIntersect
features = l.getFeatures(QgsFeatureRequest() \
.setFilterRect(searchRect) \
.setFlags(flags))
feature = QgsFeature()
while features.nextFeature(feature):
s.features.append(QgsFeature(feature))
self.mLocationInfoModel.addSourceValues(s)
s = ""
pass
Set the cursor lcation to be loaded.
:param crs: QgsCoordinateReferenceSystem
:param point: QgsPointXY
assert isinstance(spatialPoint, SpatialPoint)
self.mLocationHistory.insert(0, spatialPoint)
if len(self.mLocationHistory) > self.mMaxPoints:
del self.mLocationHistory[self.mMaxPoints:]
if self.mCrs is None:
self.setCrs(spatialPoint.crs())
self.updateCursorLocationInfo()
pt = self.cursorLocation()
if isinstance(pt, SpatialPoint):
pt = pt.toCrs(self.mCrs)
self.tbX.setText('{}'.format(pt.x()))
self.tbY.setText('{}'.format(pt.y()))
self.setCanvases([mapCanvas])
def setCanvases(self, mapCanvases):
assert isinstance(mapCanvases, list)
for c in mapCanvases:
assert isinstance(c, QgsMapCanvas)
if len(mapCanvases) == 0:
self.setCrs(None)
else:
setNew = True
for c in mapCanvases:
if c in self.mCanvases:
setNew = False
if setNew:
self.setCrs(mapCanvases[0].mapSettings().destinationCrs())
self.mCanvases = mapCanvases
"""
Set the coordinate reference system in which coordinates are shown
:param crs:
:return:
"""
assert isinstance(crs, QgsCoordinateReferenceSystem)
if crs != self.mCrs:
self.mCrs = crs
self.btnCrs.setCrs(crs)
"""
Returns the last location that was set.
"""
if len(self.mLocationHistory) > 0:
return self.mLocationHistory[0]
else:
return None, None
class Resulthandler(QObject):
def __init__(self):
super(Resulthandler, self).__init__()
def onResult(self, *args):
print(args)
R = Resulthandler()