diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py index 6fa2c6db713730c63e4ea3d49e8f7702e41be3ef..c306218f9aafde3ce39806643b903b4f0d52fcd0 100644 --- a/timeseriesviewer/main.py +++ b/timeseriesviewer/main.py @@ -1,14 +1,13 @@ # -*- coding: utf-8 -*- """ /*************************************************************************** - EnMAPBox - A QGIS plugin - EnMAP-Box V3 + HUB TimeSeriesViewer + A QGIS based time series viewer ------------------- begin : 2015-08-20 git sha : $Format:%H$ - copyright : (C) 2015 by HU-Berlin - email : bj@geo.hu-berlin.de + copyright : (C) 2017 by HU-Berlin + email : benjamin.jakimow@geo.hu-berlin.de ***************************************************************************/ /*************************************************************************** @@ -23,38 +22,19 @@ # Import the code for the dialog import os, sys, re, fnmatch, collections, copy, traceback, six +import logging +logger = logging.getLogger(__name__) from qgis.core import * -#os.environ['PATH'] += os.pathsep + r'C:\OSGeo4W64\bin' -from osgeo import gdal, ogr, osr, gdal_array +from timeseriesviewer.utils import * -DEBUG = True -import qgis.analysis -try: - from qgis.gui import * - import qgis - qgis_available = True - - #import console.console_output - #console.show_console() - #sys.stdout = console.console_output.writeOut() - #sys.stderr = console.console_output.writeOut() -except: - print('Can not find QGIS instance') - qgis_available = False +DEBUG = True import numpy as np - -import sys, bisect, multiprocessing, site -from PyQt4.QtCore import * -from PyQt4.QtGui import * -from PyQt4.uic.Compiler.qtproxies import QtGui, QtCore -import code -import codecs - +import multiprocessing #abbreviations -from timeseriesviewer import jp, mkdir, DIR_SITE_PACKAGES, file_search, dprint +from timeseriesviewer import jp, mkdir, DIR_SITE_PACKAGES, file_search from timeseriesviewer.timeseries import * @@ -72,93 +52,6 @@ if os.path.exists(path): import pyqtgraph as pg -class SpatialExtent(QgsRectangle): - """ - Object to keep QgsRectangle and QgsCoordinateReferenceSystem together - """ - @staticmethod - def fromMapCanvas(mapCanvas): - assert isinstance(mapCanvas, QgsMapCanvas) - extent = mapCanvas.extent() - crs = mapCanvas.mapSettings().destinationCrs() - return SpatialExtent(crs, extent) - - @staticmethod - def fromMapLayer(lyr): - assert isinstance(lyr, QgsMapLayer) - extent = lyr.extent() - crs = lyr.crs() - return SpatialExtent(crs, extent) - - - def __init__(self, crs, *args): - assert isinstance(crs, QgsCoordinateReferenceSystem) - super(SpatialExtent, self).__init__(*args) - self.mCrs = crs - - def setCrs(self, crs): - assert isinstance(crs, QgsCoordinateReferenceSystem) - self.mCrs = crs - - def crs(self): - return self.mCrs - - def toCrs(self, crs): - assert isinstance(crs, QgsCoordinateReferenceSystem) - box = QgsRectangle(self) - if self.mCrs != crs: - trans = QgsCoordinateTransform(self.mCrs, crs) - box = trans.transformBoundingBox(box) - return SpatialExtent(crs, box) - - def __copy__(self): - return SpatialExtent(self.crs(), QgsRectangle(self)) - - def combineExtentWith(self, *args): - if args is None: - return - elif isinstance(args[0], SpatialExtent): - extent2 = args[0].toCrs(self.crs()) - self.combineExtentWith(QgsRectangle(extent2)) - else: - super(SpatialExtent, self).combineExtentWith(*args) - - def setCenter(self, centerPoint, crs=None): - - if crs and crs != self.crs(): - trans = QgsCoordinateTransform(crs, self.crs()) - centerPoint = trans.transform(centerPoint) - - delta = centerPoint - self.center() - self.setXMaximum(self.xMaximum() + delta.x()) - self.setXMinimum(self.xMinimum() + delta.x()) - self.setYMaximum(self.yMaximum() + delta.y()) - self.setYMinimum(self.yMinimum() + delta.y()) - - - def __cmp__(self, other): - if other is None: return 1 - s = "" - - def __eq__(self, other): - s = "" - - def __sub__(self, other): - raise NotImplementedError() - - def __mul__(self, other): - raise NotImplementedError() - - def upperLeft(self): - return self.xMinimum(), self.yMaximum() - - def lowerRight(self): - return self.xMaximum(), self.yMinimum() - - def __repr__(self): - - return '{} {} {}'.format(self.upperLeft(), self.lowerRight(), self.crs().authid()) - class TsvMimeDataUtils(QObject): def __init__(self, mimeData): assert isinstance(mimeData, QMimeData) @@ -1079,7 +972,7 @@ class TimeSeriesViewer: D.actionIdentifyMapLayers.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('identifyMapLayers')) D.actionAddMapView.triggered.connect(self.spatialTemporalVis.createMapView) - D.actionAddTSD.triggered.connect(lambda : self.ua_addTSImages()) + D.actionAddTSD.triggered.connect(lambda : self.addTimeSeriesImages()) D.actionRemoveTSD.triggered.connect(lambda: self.TS.removeDates(self.ui.dockTimeSeries.selectedTimeSeriesDates())) D.actionRefresh.triggered.connect(self.spatialTemporalVis.refresh) D.actionLoadTS.triggered.connect(self.loadTimeSeries) @@ -1304,10 +1197,13 @@ class TimeSeriesViewer: # w.widget().deleteLater() QApplication.processEvents() - def ua_addTSImages(self, files=None): + def addTimeSeriesImages(self, files=None): if files is None: files = QFileDialog.getOpenFileNames() + #collect sublayers, if existing + + if files: self.TS.addFiles(files) @@ -1423,8 +1319,8 @@ def run_tests(): filesImgLS = file_search(dirSrcLS, '20*_BOA.vrt') filesImgRE = file_search(dirSrcRE, '*.vrt', recursive=True) #filesMsk = file_search(dirSrc, '2014*_Msk.vrt') - S.ua_addTSImages(files=filesImgLS[0:2]) - S.ua_addTSImages(files=filesImgRE[0:2]) + S.addTimeSeriesImages(files=filesImgLS[0:2]) + S.addTimeSeriesImages(files=filesImgRE[0:2]) #S.ua_addTSImages(files=filesImgLS) #S.ua_addTSImages(files=filesImgRE) #S.ua_loadExampleTS() @@ -1455,7 +1351,7 @@ def run_tests(): dirSrcLS = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted' filesImgLS = file_search(dirSrcLS, '2014*_BOA.vrt') filesMsk = file_search(dirSrcLS, '2014*_Msk.vrt') - S.ua_addTSImages(files=filesImgLS) + S.addTimeSeriesImages(files=filesImgLS) S.ua_addTSMasks(files=filesMsk) #S.ua_addView(bands=[4,5,3]) diff --git a/timeseriesviewer/sandbox.py b/timeseriesviewer/sandbox.py index e1d687e6fdf620660356bbc4807c9e8ec2e75a2b..3b65e27ee89c42d1af945df172ad562160dccdba 100644 --- a/timeseriesviewer/sandbox.py +++ b/timeseriesviewer/sandbox.py @@ -1,5 +1,7 @@ from __future__ import absolute_import import six, sys, os, gc, re, collections, site, inspect +import logging +logger = logging.getLogger(__name__) from osgeo import gdal, ogr from qgis import * @@ -9,6 +11,7 @@ from PyQt4.QtGui import * from PyQt4.QtCore import * from timeseriesviewer import * +from timeseriesviewer.utils import * class SandboxObjects(object): @@ -58,9 +61,24 @@ def sandboxGui(): if True: #load Sentinel-2 searchDir = r'H:\Sentinel2' - files = file_search(searchDir, 'S2*.xml', recursive=True) - files = [f+':20m' for f in files] - S.loadImageFiles(files[0:5]) + files = file_search(searchDir, '*MSIL1C.xml', recursive=True) + + subLayerEndings = getSubLayerEndings(files) + if len(subLayerEndings) > 0: + layerDefinitions = [] + for i, subLayer in enumerate(subLayerEndings): + ldef = QgsSublayersDialog.LayerDefinition() + ldef.layerName = subLayer + ldef.layerId = i + layerDefinitions.append(ldef) + + d = QgsSublayersDialog(QgsSublayersDialog.Gdal, 'Select Sublayers') + d.populateLayerTable(layerDefinitions) + d.exec_() + subLayerEndings = [l.layerName for l in d.selection()] + + files = filterSubLayers(files, subLayerEndings) + S.loadImageFiles(files) if False: diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py index fd0fca056eeb6ae11003ac5e4b329680909b3b1d..9f6abe08bd0ebf12fa41d84a37a704cbb6f4ec98 100644 --- a/timeseriesviewer/utils.py +++ b/timeseriesviewer/utils.py @@ -1,7 +1,180 @@ from collections import defaultdict +from qgis.core import * +from qgis.gui import * +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from osgeo import gdal + import weakref + +def fileSizeString(num, suffix='B', div=1000): + """ + Returns a human-readable file size string. + thanks to Fred Cirera + http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size + :param num: number in bytes + :param suffix: 'B' for bytes by default. + :param div: divisor of num, 1000 by default. + :return: the file size string + """ + for unit in ['','K','M','G','T','P','E','Z']: + if abs(num) < div: + return "{:3.1f}{}{}".format(num, unit, suffix) + num /= div + return "{:.1f} {}{}".format(num, unit, suffix) + +class SpatialPoint(QgsPoint): + """ + Object to keep QgsPoint and QgsCoordinateReferenceSystem together + """ + + @staticmethod + def fromMapCanvasCenter(mapCanvas): + assert isinstance(mapCanvas, QgsMapCanvas) + crs = mapCanvas.mapSettings().destinationCrs() + return SpatialPoint(crs, mapCanvas.center()) + + def __init__(self, crs, *args): + assert isinstance(crs, QgsCoordinateReferenceSystem) + super(SpatialPoint, self).__init__(*args) + self.mCrs = crs + + def setCrs(self, crs): + assert isinstance(crs, QgsCoordinateReferenceSystem) + self.mCrs = crs + + def crs(self): + return self.mCrs + + def toCrs(self, crs): + assert isinstance(crs, QgsCoordinateReferenceSystem) + pt = QgsPoint(self) + if self.mCrs != crs: + trans = QgsCoordinateTransform(self.mCrs, crs) + pt = trans.transform(pt) + return SpatialPoint(crs, pt) + + def __copy__(self): + return SpatialExtent(self.crs(), QgsRectangle(self)) + + def __repr__(self): + return '{} {} {}'.format(self.x(), self.y(), self.crs().authid()) + + +def findParent(qObject, parentType, checkInstance = False): + parent = qObject.parent() + if checkInstance: + while parent != None and not isinstance(parent, parentType): + parent = parent.parent() + else: + while parent != None and type(parent) != parentType: + parent = parent.parent() + return parent + +class SpatialExtent(QgsRectangle): + """ + Object to keep QgsRectangle and QgsCoordinateReferenceSystem together + """ + @staticmethod + def fromMapCanvas(mapCanvas, fullExtent=False): + assert isinstance(mapCanvas, QgsMapCanvas) + + if fullExtent: + extent = mapCanvas.fullExtent() + else: + extent = mapCanvas.extent() + crs = mapCanvas.mapSettings().destinationCrs() + return SpatialExtent(crs, extent) + + @staticmethod + def fromLayer(mapLayer): + assert isinstance(mapLayer, QgsMapLayer) + extent = mapLayer.extent() + crs = mapLayer.crs() + return SpatialExtent(crs, extent) + + def __init__(self, crs, *args): + assert isinstance(crs, QgsCoordinateReferenceSystem) + super(SpatialExtent, self).__init__(*args) + self.mCrs = crs + + def setCrs(self, crs): + assert isinstance(crs, QgsCoordinateReferenceSystem) + self.mCrs = crs + + def crs(self): + return self.mCrs + + def toCrs(self, crs): + assert isinstance(crs, QgsCoordinateReferenceSystem) + box = QgsRectangle(self) + if self.mCrs != crs: + trans = QgsCoordinateTransform(self.mCrs, crs) + box = trans.transformBoundingBox(box) + return SpatialExtent(crs, box) + + def __copy__(self): + return SpatialExtent(self.crs(), QgsRectangle(self)) + + def combineExtentWith(self, *args): + if args is None: + return + elif isinstance(args[0], SpatialExtent): + extent2 = args[0].toCrs(self.crs()) + self.combineExtentWith(QgsRectangle(extent2)) + else: + super(SpatialExtent, self).combineExtentWith(*args) + + return self + + def setCenter(self, centerPoint, crs=None): + + if crs and crs != self.crs(): + trans = QgsCoordinateTransform(crs, self.crs()) + centerPoint = trans.transform(centerPoint) + + delta = centerPoint - self.center() + self.setXMaximum(self.xMaximum() + delta.x()) + self.setXMinimum(self.xMinimum() + delta.x()) + self.setYMaximum(self.yMaximum() + delta.y()) + self.setYMinimum(self.yMinimum() + delta.y()) + + return self + + def __cmp__(self, other): + if other is None: return 1 + s = "" + + def __eq__(self, other): + s = "" + + def __sub__(self, other): + raise NotImplementedError() + + def __mul__(self, other): + raise NotImplementedError() + + def upperRight(self): + return self.xMaximum(), self.yMaximum() + + def upperLeft(self): + return self.xMinimum(), self.yMaximum() + + def lowerRight(self): + return self.xMaximum(), self.yMinimum() + + def lowerLeft(self): + return self.xMinimum(), self.yMinimum() + + + def __repr__(self): + + return '{} {} {}'.format(self.upperLeft(), self.lowerRight(), self.crs().authid()) + + class KeepRefs(object): __refs__ = defaultdict(list) def __init__(self): @@ -12,4 +185,46 @@ class KeepRefs(object): for inst_ref in cls.__refs__[cls]: inst = inst_ref() if inst is not None: - yield inst \ No newline at end of file + yield inst + + + +def filterSubLayers(filePaths, subLayerEndings): + """ + Returns sub layers endings from all gdal Datasets within filePaths + :param filePaths: + :param subLayerEndings: + :return: + """ + results = [] + if len(subLayerEndings) == 0: + return filePaths[:] + + for path in filePaths: + try: + ds = gdal.Open(path) + if ds.RasterCount == 0: + for s in ds.GetSubDatasets(): + for ending in subLayerEndings: + if s[0].endswith(ending): + results.append(s[0]) + else: + results.append(path) + except: + pass + return results + +def getSubLayerEndings(files): + subLayerEndings = [] + for file in files: + try: + ds = gdal.Open(file) + for subLayer in ds.GetSubDatasets(): + ending = subLayer[0].split(':')[-2:] + if ending not in subLayerEndings: + subLayerEndings.append(':'.join(ending)) + except: + s = "" + pass + + return subLayerEndings