Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
# calculate pixel indices to load
for tpId, wkt in task.mGeometries.items():
geom = QgsGeometry.fromWkt(wkt)
assert isinstance(geom, QgsGeometry)
pt = geom.centroid().asPoint()
px = geo2px(pt, gt)
if px.x() < 0 or px.x() > ns or px.y() < 0 or px.y() > nl:
task.mERRORS.append('TemporalProfile {} is out of image bounds: {} = pixel {}'.format(tpId, geom, px))
continue
task.mRESULTS[tpId] = {'px_x':px.x(),
'px_y':px.y(),
'geo_x':pt.x(),
'geo_y':px.y(),
'gt':gt}
pxIndices[tpId] = px
# todo: implement load balancing
for j, bandIndex in enumerate([b for b in task.mBandIndices if b >= 0 and b < nb]):
band = ds.GetRasterBand(bandIndex + 1)
assert isinstance(band, gdal.Band)
no_data = band.GetNoDataValue()
bandName = 'b{}'.format(bandIndex + 1)
for tpId, px in pxIndices.items():
assert isinstance(px, QPoint)
value = band.ReadAsArray(px.x(), px.y(), 1, 1).flatten()[0]
if no_data and value == no_data:
value = np.NaN
task.mRESULTS[tpId][bandName] = value
except Exception as ex:
task.mERRORS.append('Error source image {}:\n{}'.format(task.mSourcePath, ex))
results.append(task)
qgsTask.setProgress(100 * (i + 1) / n)
return pickle.dumps(results)
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
class TemporalProfilePlotDataItem(pg.PlotDataItem):
def __init__(self, plotStyle, parent=None):
assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
super(TemporalProfilePlotDataItem, self).__init__([], [], parent=parent)
self.menu = None
#self.setFlags(QGraphicsItem.ItemIsSelectable)
self.mPlotStyle = plotStyle
self.setAcceptedMouseButtons(Qt.LeftButton | Qt.RightButton)
self.mPlotStyle.sigUpdated.connect(self.updateDataAndStyle)
self.updateStyle()
# On right-click, raise the context menu
def mouseClickEvent(self, ev):
if ev.button() == QtCore.Qt.RightButton:
if self.raiseContextMenu(ev):
ev.accept()
def raiseContextMenu(self, ev):
menu = self.getContextMenus()
# Let the scene add on to the end of our context menu
# (this is optional)
menu = self.scene().addParentContextMenus(self, menu, ev)
pos = ev.screenPos()
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
return True
# This method will be called when this item's _children_ want to raise
# a context menu that includes their parents' menus.
def getContextMenus(self, event=None):
if self.menu is None:
self.menu = QMenu()
self.menu.setTitle(self.name + " options..")
green = QAction("Turn green", self.menu)
green.triggered.connect(self.setGreen)
self.menu.addAction(green)
self.menu.green = green
blue = QAction("Turn blue", self.menu)
blue.triggered.connect(self.setBlue)
self.menu.addAction(blue)
self.menu.green = blue
alpha = QWidgetAction(self.menu)
alphaSlider = QSlider()
alphaSlider.setOrientation(QtCore.Qt.Horizontal)
alphaSlider.setMaximum(255)
alphaSlider.setValue(255)
alphaSlider.valueChanged.connect(self.setAlpha)
alpha.setDefaultWidget(alphaSlider)
self.menu.addAction(alpha)
self.menu.alpha = alpha
self.menu.alphaSlider = alphaSlider
return self.menu
def updateDataAndStyle(self):
TP = self.mPlotStyle.temporalProfile()
sensor = self.mPlotStyle.sensor()
if isinstance(TP, TemporalProfile) and isinstance(sensor, SensorInstrument):
x, y = TP.dataFromExpression(self.mPlotStyle.sensor(), self.mPlotStyle.expression(), dateType='date')

Benjamin Jakimow
committed
if np.any(np.isfinite(y)):
self.setData(x=x, y=y, connect='finite')
else:
self.setData(x=[], y=[]) # dummy
else:
self.setData(x=[], y=[]) # dummy for empty data
self.updateStyle()

Benjamin Jakimow
committed
def updateStyle(self):
"""
Updates visibility properties
"""
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
self.setVisible(self.mPlotStyle.isVisible())
self.setSymbol(self.mPlotStyle.markerSymbol)
self.setSymbolSize(self.mPlotStyle.markerSize)
self.setSymbolBrush(self.mPlotStyle.markerBrush)
self.setSymbolPen(self.mPlotStyle.markerPen)
self.setPen(self.mPlotStyle.linePen)
self.update()
def setClickable(self, b, width=None):
assert isinstance(b, bool)
self.curve.setClickable(b, width=width)
def setColor(self, color):
if not isinstance(color, QColor):
color = QColor(color)
self.setPen(color)
def pen(self):
return fn.mkPen(self.opts['pen'])
def color(self):
return self.pen().color()
def setLineWidth(self, width):
pen = pg.mkPen(self.opts['pen'])
assert isinstance(pen, QPen)
pen.setWidth(width)
self.setPen(pen)
class TemporalProfileLayer(QgsVectorLayer):
"""
A collection to store the TemporalProfile data delivered by a PixelLoader
"""
#sigSensorAdded = pyqtSignal(SensorInstrument)
#sigSensorRemoved = pyqtSignal(SensorInstrument)
#sigPixelAdded = pyqtSignal()
#sigPixelRemoved = pyqtSignal()
sigTemporalProfilesAdded = pyqtSignal(list)
sigTemporalProfilesRemoved = pyqtSignal(list)
sigMaxProfilesChanged = pyqtSignal(int)
def __init__(self, timeSeries:TimeSeries, uri=None, name='Temporal Profiles'):
lyrOptions = QgsVectorLayer.LayerOptions(loadDefaultStyle=False, readExtentFromXml=False)
if uri is None:
# create a new, empty backend
# existing_vsi_files = vsiSpeclibs()
existing_vsi_files = []
# todo:
assert isinstance(existing_vsi_files, list)
i = 0
_name = name.replace(' ', '_')
uri = (pathlib.Path(VSI_DIR) / '{}.gpkg'.format(_name)).as_posix()
while not ogr.Open(uri) is None:
uri = (pathlib.Path(VSI_DIR) / '{}{:03}.gpkg'.format(_name, i)).as_posix()
drv = ogr.GetDriverByName('GPKG')
assert isinstance(drv, ogr.Driver)
co = ['VERSION=AUTO']
dsSrc = drv.CreateDataSource(uri, options=co)
assert isinstance(dsSrc, ogr.DataSource)
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
co = ['GEOMETRY_NAME=geom',
'GEOMETRY_NULLABLE=YES',

Benjamin Jakimow
committed
'FID={}'.format(FN_ID)
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
]
lyr = dsSrc.CreateLayer(name, srs=srs, geom_type=ogr.wkbPoint, options=co)
assert isinstance(lyr, ogr.Layer)
ldefn = lyr.GetLayerDefn()
assert isinstance(ldefn, ogr.FeatureDefn)
dsSrc.FlushCache()
else:
dsSrc = ogr.Open(uri)
assert isinstance(dsSrc, ogr.DataSource)
names = [dsSrc.GetLayerByIndex(i).GetName() for i in range(dsSrc.GetLayerCount())]
i = names.index(name)
lyr = dsSrc.GetLayer(i)
# consistency check
uri2 = '{}|{}'.format(dsSrc.GetName(), lyr.GetName())
assert QgsVectorLayer(uri2).isValid()
super(TemporalProfileLayer, self).__init__(uri2, name, 'ogr', lyrOptions)
"""
uri = 'Point?crs={}'.format(crs.authid())
lyrOptions = QgsVectorLayer.LayerOptions(loadDefaultStyle=False, readExtentFromXml=False)
super(TemporalProfileLayer, self).__init__(uri, name, 'memory', lyrOptions)
self.mProfiles = OrderedDict()
self.mTimeSeries = timeSeries
#symbol = QgsFillSymbol.createSimple({'style': 'no', 'color': 'red', 'outline_color': 'black'})
#self.mLocations.renderer().setSymbol(symbol)

Benjamin Jakimow
committed
#self.mNextID = 1
self.TS = None
fields = QgsFields()

Benjamin Jakimow
committed
#fields.append(createQgsField(FN_ID, self.mNextID))
fields.append(createQgsField(FN_NAME, ''))
fields.append(createQgsField(FN_X, 0.0, comment='Longitude'))
fields.append(createQgsField(FN_Y, 0.0, comment='Latitude'))
#fields.append(createQgsField(FN_N_TOTAL, 0, comment='Total number of band values'))
#fields.append(createQgsField(FN_N_NODATA,0, comment='Total of no-data values.'))
#fields.append(createQgsField(FN_N_LOADED, 0, comment='Loaded valid band values.'))
#fields.append(createQgsField(FN_N_LOADED_PERCENT,0.0, comment='Loading progress (%)'))
assert self.startEditing()
assert self.dataProvider().addAttributes(fields)
assert self.commitChanges()
self.initConditionalStyles()
self.committedFeaturesAdded.connect(self.onFeaturesAdded)
self.committedFeaturesRemoved.connect(self.onFeaturesRemoved)
def __getitem__(self, slice):
return list(self.mProfiles.values())[slice]
def loadMissingData(self, backgroundProcess=False):
assert isinstance(self.mTimeSeries, TimeSeries)
# Get or create the TimeSeriesProfiles which will store the loaded values
tasks = []
theGeometries = []
# Define which (new) bands need to be loaded for each sensor
LUT_bandIndices = dict()
LUT_bandIndices[sensor] = list(range(sensor.nb))
PL = PixelLoader()
PL.sigPixelLoaded.connect(self.addPixelLoaderResult)

Benjamin Jakimow
committed
# update new / existing points
for tsd in self.mTimeSeries:
assert isinstance(tsd, TimeSeriesDate)
requiredIndexKeys = [bandIndex2bandKey(b) for b in requiredIndices]
TPs = []
missingIndices = set()
for TP in self.mProfiles.values():
assert isinstance(TP, TemporalProfile)
dataValues = TP.mData[tsd]
existingKeys = list(dataValues.keys())
missingIdx = [bandKey2bandIndex(k) for k in requiredIndexKeys if k not in existingKeys]
if len(missingIdx) > 0:
TPs.append(TP)
missingIndices.union(set(missingIdx))
if len(TPs) > 0:
theGeometries = [tp.coordinate() for tp in TPs]
theIDs = [tp.id() for tp in TPs]
for pathImg in tsd.sourceUris():
task = PixelLoaderTask(pathImg, theGeometries,
bandIndices=requiredIndices,
temporalProfileIDs=theIDs)
tasks.append(task)
if len(tasks) > 0:
if backgroundProcess:
PL.startLoading(tasks)
else:
import eotimeseriesviewer.pixelloader
dump = pickle.dumps(tasks)
tasks =pickle.loads(eotimeseriesviewer.pixelloader.doLoaderTask(eotimeseriesviewer.pixelloader.TaskMock(), dump))
else:
if DEBUG:
print('Data for geometries already loaded')
s = ""
def saveTemporalProfiles(self, pathVector, sep='\t'):
if pathVector is None or len(pathVector) == 0:
global DEFAULT_SAVE_PATH
if DEFAULT_SAVE_PATH == None:
DEFAULT_SAVE_PATH = 'temporalprofiles.shp'
d = os.path.dirname(DEFAULT_SAVE_PATH)
filters = QgsProviderRegistry.instance().fileVectorFilters()
pathVector, filter = QFileDialog.getSaveFileName(None, 'Save {}'.format(self.name()), DEFAULT_SAVE_PATH,
filter=filters)
if len(pathVector) == 0:
return None
else:
DEFAULT_SAVE_PATH = pathVector
drvName = QgsVectorFileWriter.driverForExtension(os.path.splitext(pathVector)[-1])
QgsVectorFileWriter.writeAsVectorFormat(self, pathVector, 'utf-8', destCRS=self.crs(), driverName=drvName)
pathCSV = os.path.splitext(pathVector)[0] + '.data.csv'
# write a flat list of profiles
csvLines.append(sep.join(['id', 'name', 'sensor', 'date', 'doy', 'sensor'] + ['b{}'.format(b+1) for b in range(nBands)]))
for p in list(self.getFeatures()):
assert isinstance(p, QgsFeature)
fid = p.id()
tp = self.mProfiles.get(fid)
if tp is None:
continue
assert isinstance(tp, TemporalProfile)
name = tp.name()
for tsd, values in tp.mData.items():
assert isinstance(tsd, TimeSeriesDate)
line = [fid, name, tsd.mSensor.name(), tsd.mDate, tsd.mDOY]
for b in range(tsd.mSensor.nb):
key = 'b{}'.format(b+1)
line.append(values.get(key))
line = ['' if v == None else str(v) for v in line]
line = sep.join([str(l) for l in line])
# write CSV file
with open(pathCSV, 'w', encoding='utf8') as f:
f.write('\n'.join(csvLines))
def timeSeries(self):
"""
Returns the TimeSeries instance.
:return: TimeSeries
"""
return self.mTimeSeries
def onFeaturesAdded(self, layerID, addedFeatures):
"""
Create a TemporalProfile object for each QgsFeature added to the backend QgsVectorLayer
:param layerID:
:param addedFeatures:
:return:
"""

Benjamin Jakimow
committed
if layerID != self.id():
s = ""
if len(addedFeatures) > 0:
temporalProfiles = []
for feature in addedFeatures:
fid = feature.id()
self.mProfiles[fid] = tp
temporalProfiles.append(tp)
if len(temporalProfiles) > 0:
pass
#self.sigTemporalProfilesAdded.emit(temporalProfiles)
def onFeaturesRemoved(self, layerID, removedFIDs):

Benjamin Jakimow
committed
if layerID != self.id():
s = ""
if len(removedFIDs) > 0:

Benjamin Jakimow
committed
removed = []
for fid in removedFIDs:

Benjamin Jakimow
committed
removed.append(self.mProfiles.pop(fid))

Benjamin Jakimow
committed
self.sigTemporalProfilesRemoved.emit(removed)
def initConditionalStyles(self):
styles = self.conditionalStyles()
assert isinstance(styles, QgsConditionalLayerStyles)
for fieldName in self.fields().names():
red = QgsConditionalStyle("@value is NULL")
red.setTextColor(QColor('red'))
styles.setFieldStyles(fieldName, [red])
#styles.setRowStyles([red])

Benjamin Jakimow
committed
def createTemporalProfiles(self, coordinates, names:list=None)->list:
"""
Creates temporal profiles
:param coordinates:
:return:
"""

Benjamin Jakimow
committed
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
if isinstance(coordinates, QgsVectorLayer):
lyr = coordinates
coordinates = []
names = []
trans = QgsCoordinateTransform()
trans.setSourceCrs(lyr.crs())
trans.setDestinationCrs(self.crs())
nameField = None
if isinstance(names, str) and names in lyr.fields().names():
nameField = names
else:
for name in lyr.fields().names():
if re.search('names?', name, re.I):
nameField = name
break
if nameField is None:
nameField = lyr.fields().names()[0]
for f in lyr.getFeatures():
assert isinstance(f, QgsFeature)
g = f.geometry()
if g.isEmpty():
continue
g = g.centroid()
assert g.transform(trans) == 0
coordinates.append(SpatialPoint(self.crs(), g.asPoint()))
names.append(f.attribute(nameField))
del trans
elif not isinstance(coordinates, list):
coordinates = [coordinates]

Benjamin Jakimow
committed
assert isinstance(coordinates, list)
if not isinstance(names, list):
n = self.featureCount()
names = []
for i in range(len(coordinates)):
names.append('Profile {}'.format(n+i+1))
assert len(coordinates) == len(names)
features = []

Benjamin Jakimow
committed
for i, (coordinate, name) in enumerate(zip(coordinates, names)):
assert isinstance(coordinate, SpatialPoint)
f = QgsFeature(self.fields())
f.setGeometry(QgsGeometry.fromPointXY(coordinate.toCrs(self.crs())))

Benjamin Jakimow
committed
#f.setAttribute(FN_ID, self.mNextID)
f.setAttribute(FN_NAME, name)
f.setAttribute(FN_X, coordinate.x())
f.setAttribute(FN_Y, coordinate.y())
#f.setAttribute(FN_N_LOADED_PERCENT, 0.0)
#f.setAttribute(FN_N_LOADED, 0)
#f.setAttribute(FN_N_TOTAL, 0)
#f.setAttribute(FN_N_NODATA, 0)

Benjamin Jakimow
committed
#self.mNextID += 1
features.append(f)

Benjamin Jakimow
committed
if len(features) == 0:
return []
b = self.isEditable()

Benjamin Jakimow
committed
newFeatures = []
def onFeaturesAdded(lid, fids):
newFeatures.extend(fids)
self.committedFeaturesAdded.connect(onFeaturesAdded)
self.beginEditCommand('Add {} profile locations'.format(len(features)))
success = self.addFeatures(features)

Benjamin Jakimow
committed
self.endEditCommand()
self.saveEdits(leaveEditable=b)

Benjamin Jakimow
committed
self.committedFeaturesAdded.disconnect(onFeaturesAdded)

Benjamin Jakimow
committed
assert self.featureCount() == len(self.mProfiles)
profiles = [self.mProfiles[f.id()] for f in newFeatures]
return profiles
def saveEdits(self, leaveEditable=False, triggerRepaint=True):
"""
function to save layer changes-
:param layer:
:param leaveEditable:
:param triggerRepaint:
"""
if not self.isEditable():
return
if not self.commitChanges():
self.commitErrors()
if leaveEditable:
self.startEditing()
if triggerRepaint:
self.triggerRepaint()
def addMissingFields(self, fields):
missingFields = []
for field in fields:
assert isinstance(field, QgsField)
i = self.dataProvider().fieldNameIndex(field.name())
if i == -1:
missingFields.append(field)
if len(missingFields) > 0:
b = self.isEditable()
self.startEditing()
self.dataProvider().addAttributes(missingFields)
self.saveEdits(leaveEditable=b)
def __len__(self):
def __iter__(self):
r = QgsFeatureRequest()
for f in self.getFeatures(r):
yield self.mProfiles[f.id()]
def __contains__(self, item):
return item in self.mProfiles.values()
def temporalProfileToLocationFeature(self, tp:TemporalProfile):
self.mLocations.selectByIds([tp.id()])
for f in self.mLocations.selectedFeatures():
assert isinstance(f, QgsFeature)
return f
return None
def fromSpatialPoint(self, spatialPoint):
""" Tests if a Temporal Profile already exists for the given spatialPoint"""
for p in list(self.mProfiles.values()):
assert isinstance(p, TemporalProfile)
if p.coordinate() == spatialPoint:
return p
"""
spatialPoint = spatialPoint.toCrs(self.crs())
unit = QgsUnitTypes.toAbbreviatedString(self.crs().mapUnits()).lower()
x = spatialPoint.x() + 0.00001
y = spatialPoint.y() + 0.
if 'degree' in unit:
dx = dy = 0.000001
else:
dx = dy = 0.1
rect = QgsRectangle(x-dx,y-dy, x+dy,y+dy)
for f in self.getFeatures(rect):
return self.mProfiles[f.id()]
return None
def removeTemporalProfiles(self, temporalProfiles):
"""
Removes temporal profiles from this collection
:param temporalProfile: TemporalProfile
"""
if isinstance(temporalProfiles, TemporalProfile):
temporalProfiles = [temporalProfiles]
assert isinstance(temporalProfiles, list)
temporalProfiles = [tp for tp in temporalProfiles if isinstance(tp, TemporalProfile) and tp.id() in self.mProfiles.keys()]
if len(temporalProfiles) > 0:
b = self.isEditable()
assert self.startEditing()
fids = [tp.mID for tp in temporalProfiles]
self.deleteFeatures(fids)
self.saveEdits(leaveEditable=b)
self.sigTemporalProfilesRemoved.emit(temporalProfiles)
def loadCoordinatesFromOgr(self, path):
"""Loads the TemporalProfiles for vector geometries in data source 'path' """
if path is None:
filters = QgsProviderRegistry.instance().fileVectorFilters()
defDir = None
if isinstance(DEFAULT_SAVE_PATH, str) and len(DEFAULT_SAVE_PATH) > 0:
defDir = os.path.dirname(DEFAULT_SAVE_PATH)
path, filter = QFileDialog.getOpenFileName(directory=defDir, filter=filters)
if isinstance(path, str) and len(path) > 0:
sourceLyr = QgsVectorLayer(path)
nameAttribute = None
fieldNames = [n.lower() for n in sourceLyr.fields().names()]
for candidate in ['name', 'id']:
if candidate in fieldNames:
nameAttribute = sourceLyr.fields().names()[fieldNames.index(candidate)]
break
if len(self.timeSeries()) == 0:
sourceLyr.selectAll()
else:
extent = self.timeSeries().maxSpatialExtent(sourceLyr.crs())
sourceLyr.selectByRect(extent)
newProfiles = []
for feature in sourceLyr.selectedFeatures():
assert isinstance(feature, QgsFeature)
geom = feature.geometry()
if isinstance(geom, QgsGeometry):
point = geom.centroid().constGet()
try:
TPs = self.createTemporalProfiles(SpatialPoint(sourceLyr.crs(), point))
for TP in TPs:
if nameAttribute:
name = feature.attribute(nameAttribute)
else:
name = 'FID {}'.format(feature.id())
TP.setName(name)
newProfiles.append(TP)
except Exception as ex:
print(ex)
def addPixelLoaderResult(self, d):
assert isinstance(d, PixelLoaderTask)
if d.success():
for fid in d.temporalProfileIDs:
TP = self.mProfiles.get(fid)

benjamin.jakimow@geo.hu-berlin.de
committed
if isinstance(TP, TemporalProfile):
TP.pullDataUpdate(d)
else:

benjamin.jakimow@geo.hu-berlin.de
committed
s = ""
def clear(self):
#todo: remove TS Profiles
#self.mTemporalProfiles.clear()
#self.sensorPxLayers.clear()
pass
class TemporalProfileTableFilterModel(QgsAttributeTableFilterModel):
def __init__(self, sourceModel, parent=None):
dummyCanvas = QgsMapCanvas(parent)
dummyCanvas.setDestinationCrs(DEFAULT_CRS)
dummyCanvas.setExtent(QgsRectangle(-180,-90,180,90))
super(TemporalProfileTableFilterModel, self).__init__(dummyCanvas, sourceModel, parent=parent)
self.mDummyCanvas = dummyCanvas
#self.setSelectedOnTop(True)
class TemporalProfileTableModel(QgsAttributeTableModel):
#sigPlotStyleChanged = pyqtSignal(SpectralProfile)
#sigAttributeRemoved = pyqtSignal(str)
#sigAttributeAdded = pyqtSignal(str)
AUTOGENERATES_COLUMNS = [FN_ID, FN_Y, FN_X]
#FN_N_LOADED, FN_N_TOTAL, FN_N_NODATA,
#FN_N_LOADED_PERCENT
def __init__(self, temporalProfileLayer=None, parent=None):
if temporalProfileLayer is None:
temporalProfileLayer = TemporalProfileLayer()
cache = QgsVectorLayerCache(temporalProfileLayer, 1000)
super(TemporalProfileTableModel, self).__init__(cache, parent)
self.mTemporalProfileLayer = temporalProfileLayer
self.mCache = cache
assert self.mCache.layer() == self.mTemporalProfileLayer
self.loadLayer()
def columnNames(self):
return self.mTemporalProfileLayer.fields().names()
def feature(self, index):
id = self.rowToId(index.row())
f = self.layer().getFeature(id)
return f
def temporalProfile(self, index):
feature = self.feature(index)
return self.mTemporalProfileLayer.temporalProfileFromFeature(feature)
def data(self, index, role=Qt.DisplayRole):
Returns Temporal Profile Layer values
:param index: QModelIndex
:param role: enum Qt.ItemDataRole
:return: value
"""
if role is None or not index.isValid():
return None
result = super(TemporalProfileTableModel, self).data(index, role=role)
return result
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
def setData(self, index, value, role=None):
"""
Sets Temporal Profile Data.
:param index: QModelIndex()
:param value: value to set
:param role: role
:return: True | False
"""
if role is None or not index.isValid():
return False
f = self.feature(index)
result = False
if value == None:
value = QVariant()
cname = self.columnNames()[index.column()]
if role == Qt.EditRole and cname not in TemporalProfileTableModel.AUTOGENERATES_COLUMNS:
i = f.fieldNameIndex(cname)
if f.attribute(i) == value:
return False
b = self.mTemporalProfileLayer.isEditable()
self.mTemporalProfileLayer.startEditing()
self.mTemporalProfileLayer.changeAttributeValue(f.id(), i, value)
self.mTemporalProfileLayer.saveEdits(leaveEditable=b)
result = True
#f = self.layer().getFeature(profile.id())
#i = f.fieldNameIndex(SpectralProfile.STYLE_FIELD)
#self.layer().changeAttributeValue(f.id(), i, value)
#result = super().setData(self.index(index.row(), self.mcnStyle), value, role=Qt.EditRole)
#if not b:
# self.layer().commitChanges()
if result:
self.dataChanged.emit(index, index, [role])
else:
result = super().setData(index, value, role=role)
return result
def headerData(self, section:int, orientation:Qt.Orientation, role:int):
data = super(TemporalProfileTableModel, self).headerData(section, orientation, role)
if role == Qt.ToolTipRole and orientation == Qt.Horizontal:
#add the field comment to column description
field = self.layer().fields().at(section)
assert isinstance(field, QgsField)
comment = field.comment()
if len(comment) > 0:
data = re.sub('</p>$', ' <i>{}</i></p>'.format(comment), data)
return data
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
def supportedDragActions(self):
return Qt.CopyAction | Qt.MoveAction
def supportedDropActions(self):
return Qt.CopyAction | Qt.MoveAction
def supportedDragActions(self):
return Qt.CopyAction
def supportedDropActions(self):
return Qt.CopyAction
def flags(self, index):
if index.isValid():
columnName = self.columnNames()[index.column()]
flags = super(TemporalProfileTableModel, self).flags(index) | Qt.ItemIsSelectable
#if index.column() == 0:
# flags = flags | Qt.ItemIsUserCheckable
if columnName in TemporalProfileTableModel.AUTOGENERATES_COLUMNS:
flags = flags ^ Qt.ItemIsEditable
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
return flags
return None
class TemporalProfileFeatureSelectionManager(QgsIFeatureSelectionManager):
def __init__(self, layer, parent=None):
s =""
super(TemporalProfileFeatureSelectionManager, self).__init__(parent)
assert isinstance(layer, QgsVectorLayer)
self.mLayer = layer
self.mLayer.selectionChanged.connect(self.selectionChanged)
def layer(self):
return self.mLayer
def deselect(self, ids):
if len(ids) > 0:
selected = [id for id in self.selectedFeatureIds() if id not in ids]
self.mLayer.deselect(ids)
self.selectionChanged.emit(selected, ids, True)
def select(self, ids):
self.mLayer.select(ids)
def selectFeatures(self, selection, command):
super(TemporalProfileFeatureSelectionManager, self).selectF
s = ""
def selectedFeatureCount(self):
return self.mLayer.selectedFeatureCount()
def selectedFeatureIds(self):
return self.mLayer.selectedFeatureIds()
def setSelectedFeatures(self, ids):
self.mLayer.selectByIds(ids)
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
class TemporalProfileTableView(QgsAttributeTableView):
def __init__(self, parent=None):
super(TemporalProfileTableView, self).__init__(parent)
#self.setSelectionBehavior(QAbstractItemView.SelectRows)
#self.setSelectionMode(QAbstractItemView.SingleSelection)
self.horizontalHeader().setSectionsMovable(True)
self.willShowContextMenu.connect(self.onWillShowContextMenu)
self.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
self.mSelectionManager = None
def setModel(self, filterModel):
super(TemporalProfileTableView, self).setModel(filterModel)
self.mSelectionManager = TemporalProfileFeatureSelectionManager(self.model().layer())
self.setFeatureSelectionManager(self.mSelectionManager)
#self.selectionModel().selectionChanged.connect(self.onSelectionChanged)
self.mContextMenuActions = []
def setContextMenuActions(self, actions:list):
self.mContextMenuActions = actions
#def contextMenuEvent(self, event):
def onWillShowContextMenu(self, menu, index):
assert isinstance(menu, QMenu)
assert isinstance(index, QModelIndex)
featureIDs = self.temporalProfileLayer().selectedFeatureIds()
if len(featureIDs) == 0 and index.isValid():
if isinstance(self.model(), QgsAttributeTableFilterModel):
index = self.model().mapToSource(index)
if index.isValid():
featureIDs.append(self.model().sourceModel().feature(index).id())
elif isinstance(self.model(), QgsAttributeTableFilterModel):
featureIDs.append(self.model().feature(index).id())
for a in self.mContextMenuActions:
menu.addAction(a)
for a in self.actions():
menu.addAction(a)
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
def temporalProfileLayer(self):
return self.model().layer()
def fidsToIndices(self, fids):
"""
Converts feature ids into FilterModel QModelIndices
:param fids: [list-of-int]
:return:
"""
if isinstance(fids, int):
fids = [fids]
assert isinstance(fids, list)
fmodel = self.model()
indices = [fmodel.fidToIndex(id) for id in fids]
return [fmodel.index(idx.row(), 0) for idx in indices]
def onRemoveFIDs(self, fids):
layer = self.temporalProfileLayer()
assert isinstance(layer, TemporalProfileLayer)
b = layer.isEditable()
layer.startEditing()
layer.deleteFeatures(fids)
layer.saveEdits(leaveEditable=b)
def dropEvent(self, event):
assert isinstance(event, QDropEvent)
mimeData = event.mimeData()
if self.model().rowCount() == 0:
index = self.model().createIndex(0,0)
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
index = self.indexAt(event.pos())
#if mimeData.hasFormat(mimedata.MDF_SPECTRALLIBRARY):
# self.model().dropMimeData(mimeData, event.dropAction(), index.row(), index.column(), index.parent())
# event.accept()
def dragEnterEvent(self, event):
assert isinstance(event, QDragEnterEvent)
#if event.mimeData().hasFormat(mimedata.MDF_SPECTRALLIBRARY):
# event.accept()
def dragMoveEvent(self, event):
assert isinstance(event, QDragMoveEvent)
#if event.mimeData().hasFormat(mimedata.MDF_SPECTRALLIBRARY):
# event.accept()
s = ""