Skip to content
Snippets Groups Projects
main.py 49.1 KiB
Newer Older
            if isinstance(mapCanvas, MapCanvas):
                tsd = mapCanvas.tsd()
            if not hasattr(self, 'cntSpectralProfile'):
                self.cntSpectralProfile = 0
            profiles = SpectralProfile.fromMapCanvas(mapCanvas, spatialPoint)
            # add metadata
            if isinstance(tsd, TimeSeriesDate):
                profiles2 = []
                sl = self.spectralLibrary()
                if isinstance(sl, SpectralLibrary):
                    for p in profiles:
                        self.cntSpectralProfile += 1
                        assert isinstance(p, SpectralProfile)
                        p2 = p.copyFieldSubset(fields=sl.fields())
                        p2.setName('Profile {} {}'.format(self.cntSpectralProfile, tsd.mDate))
                        p2.setAttribute('date', '{}'.format(tsd.mDate))
                        p2.setAttribute('doy', int(tsd.mDOY))
                        p2.setAttribute('sensor', tsd.mSensor.name())
                        profiles2.append(p2)
                    self.ui.dockSpectralLibrary.SLW.setCurrentSpectra(profiles2)
        elif mapToolKey == MapTools.CursorLocation:

            self.ui.dockCursorLocation.loadCursorLocation(spatialPoint, mapCanvas)

    def messageBar(self)->QgsMessageBar:
        """
        Returns the QgsMessageBar that is used to show messages in the TimeSeriesViewer UI.
        :return: QgsMessageBar
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def loadTimeSeriesDefinition(self, path:str=None, n_max:int=None, runAsync=True):
        """
        Loads a time series definition file
        :param path:
        :param n_max:
        :return:
        """
        s = settings()
        if not (isinstance(path, str) and os.path.isfile(path)):
            defFile = s.value('file_ts_definition')
            defDir = None
            if defFile is not None:
                defDir = os.path.dirname(defFile)

            filters = "CSV (*.csv *.txt);;" + \
                      "All files (*.*)"

            path, filter = QFileDialog.getOpenFileName(caption='Load Time Series definition', directory=defDir, filter=filters)
        if path is not None and os.path.exists(path):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            s.setValue('file_ts_definition', path)
            progressDialog = self._createProgressDialog()
            self.mTimeSeries.loadFromFile(path, n_max=n_max, progressDialog=progressDialog, runAsync=runAsync)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def createMapView(self, name:str=None)->MapView:
        return self.ui.dockMapViews.createMapView(name=name)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def mapViews(self)->typing.List[MapView]:
        """
        Returns all MapViews
        :return: [list-of-MapViews]
        """
        return self.ui.dockMapViews[:]
    def icon(self)->QIcon:
        """
        Returns the EO Time Series Viewer icon
        :return: QIcon
        """
        import eotimeseriesviewer
        return eotimeseriesviewer.icon()
    def temporalProfiles(self)->list:
        """
        Returns collected temporal profiles
        :return: [list-of-TemporalProfiles]
        """
        return self.spectralTemporalVis.temporalProfileLayer()[:]

    def logMessage(self, message:str, tag:str, level):
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        """
        if tag == LOG_MESSAGE_TAG and level in [Qgis.Critical, Qgis.Warning]:
            self.messageBar().pushMessage(tag, message, level)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def onSensorAdded(self, sensor:SensorInstrument):

        knownName = eotsvSettings.sensorName(sensor.id())
        sensor.sigNameChanged.connect(lambda *args, s=sensor: self.onSensorNameChanged(sensor))

        if isinstance(knownName, str) and len(knownName) > 0:
            sensor.setName(knownName)
        else:
            self.onSensorNameChanged(sensor) # save the sensor name to the settings

    def onSensorNameChanged(self, sensor:SensorInstrument):
        # save changed names to settings
        from eotimeseriesviewer.settings import saveSensorName
        if sensor in self.sensors():
            saveSensorName(sensor)
    def onTimeSeriesChanged(self, *args):
        if not self.mSpatialMapExtentInitialized:
                if len(self.ui.dockMapViews) == 0:
                    self.ui.dockMapViews.createMapView()
                extent = self.timeSeries().maxSpatialExtent()
                if isinstance(extent, SpatialExtent):
                    self.ui.dockMapViews.setCrs(extent.crs())
                    self.ui.mMapWidget.setSpatialExtent(extent)
                    self.mSpatialMapExtentInitialized = True
                else:
                    self.mSpatialMapExtentInitialized = False
                    print('Failed to calculate max. spatial extent of TimeSeries with length {}'.format(len(self.timeSeries())))
                lastDate = self.ui.mMapWidget.currentDate()
                if lastDate:
                    tsd = self.timeSeries().findDate(lastDate)
                else:
                    tsd = self.timeSeries()[0]
                self.setCurrentDate(tsd)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def mapWidget(self)->MapWidget:
        """
        Returns the MapWidget that contains all map canvases.
        :return: MapWidget
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def saveTimeSeriesDefinition(self):
        s = settings()
        defFile = s.value('FILE_TS_DEFINITION')
        if defFile is not None:
            defFile = os.path.dirname(defFile)

        filters = "CSV (*.csv *.txt);;" + \
                  "All files (*.*)"
        path, filter = QFileDialog.getSaveFileName(caption='Save Time Series definition', filter=filters, directory=defFile)
        path = self.mTimeSeries.saveToFile(path)
            s.setValue('FILE_TS_DEFINITION', path)
    def loadTimeSeriesStack(self):

        from eotimeseriesviewer.stackedbandinput import StackedBandInputDialog

        d = StackedBandInputDialog(parent=self.ui)
        if d.exec_() == QDialog.Accepted:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            writtenFiles = d.saveImages()
            self.addTimeSeriesImages(writtenFiles)

    def loadExampleTimeSeries(self, n:int=None, loadAsync=True):
        """
        Loads an example time series
        :param n: int, max. number of images to load. Useful for developer test-cases
        """
        exampleDataDir = os.path.dirname(example.__file__)
        rasterFiles = list(file_search(exampleDataDir, '*.tif', recursive=True))
        vectorFiles = list(file_search(exampleDataDir, re.compile(r'.*\.(gpkg|shp)$'), recursive=True))
            n = len(rasterFiles)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        # ensure valid inputs for n
        n = min(n, len(rasterFiles))
        self.addTimeSeriesImages(rasterFiles[0:n], loadAsync=loadAsync)

        if len(vectorFiles) > 0:

            # make polygons transparent

            self.addVectorData(vectorFiles)

            for lyr in QgsProject.instance().mapLayers().values():
                if isinstance(lyr, QgsVectorLayer) and lyr.source() in vectorFiles:
                    renderer = lyr.renderer()
                    if lyr.geometryType() == QgsWkbTypes.PolygonGeometry and isinstance(renderer, QgsSingleSymbolRenderer):
                        renderer = renderer.clone()
                        symbol = renderer.symbol()
                        if isinstance(symbol, QgsFillSymbol):
                            symbol.setOpacity(0.25)
                        lyr.setRenderer(renderer)
                    s = ""

    def timeSeries(self)->TimeSeries:
        """
        Returns the TimeSeries instance.
        :return: TimeSeries
        """
        return self.mTimeSeries
unknown's avatar
unknown committed

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
unknown's avatar
unknown committed


    def unload(self):
        """Removes the plugin menu item and icon """
        self.iface.removeToolBarIcon(self.action)
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def show(self):
        self.ui.show()

unknown's avatar
unknown committed
    def clearLayoutWidgets(self, L):
        if L is not None:
            while L.count():
                w = L.takeAt(0)
                if w.widget():
                    w.widget().deleteLater()
unknown's avatar
unknown committed
                #if w is not None:
                #    w.widget().deleteLater()
unknown's avatar
unknown committed

    def addVectorData(self, files=None)->list:
        """
        Adds vector data
        :param files: vector layer sources
        :return: [list-of-QgsVectorLayers]
        """
        vectorLayers = []
unknown's avatar
unknown committed
        if files is None:
            s = settings()
            defDir = s.value('DIR_FILESEARCH')
            filters = QgsProviderRegistry.instance().fileVectorFilters()
            files, filter = QFileDialog.getOpenFileNames(directory=defDir, filter=filters)
            if len(files) > 0 and os.path.exists(files[0]):
                dn = os.path.dirname(files[0])
                s.setValue('DIR_FILESEARCH', dn)

        if files:
            from eotimeseriesviewer.mapvisualization import MapView
            from .externals.qps.layerproperties import subLayers
            for f in files:
                vectorLayers.extend(subLayers(QgsVectorLayer(f)))

            if len(vectorLayers) > 0:
                QgsProject.instance().addMapLayers(vectorLayers)
                for mapView in self.mapViews():
                    assert isinstance(mapView, MapView)
                    for l in vectorLayers:
                        mapView.addLayer(l)

                    break # add to first mapview only
        return vectorLayers
    def addTimeSeriesImages(self, files: list, loadAsync=True):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        """
        Adds images to the time series
        :param files:
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            defDir = s.value('dir_datasources')
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
            filters = QgsProviderRegistry.instance().fileRasterFilters()
            files, filter = QFileDialog.getOpenFileNames(directory=defDir, filter=filters)

            if len(files) > 0 and os.path.exists(files[0]):
                dn = os.path.dirname(files[0])
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                s.setValue('dir_datasources', dn)
unknown's avatar
unknown committed
        if files:
            progressDialog = self._createProgressDialog()
            progressDialog.setRange(0, len(files))
            progressDialog.setLabelText('Start loading {} images....'.format(len(files)))

            self.mTimeSeries.addSources(files, progressDialog=progressDialog, runAsync=loadAsync)
unknown's avatar
unknown committed

    def clearTimeSeries(self):
unknown's avatar
unknown committed

        self.mTimeSeries.beginResetModel()

class SaveAllMapsDialog(QDialog, loadUI('saveallmapsdialog.ui')):


    def __init__(self, parent=None):

        super(SaveAllMapsDialog, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('Save Maps')
        assert isinstance(self.fileWidget, QgsFileWidget)
        assert isinstance(self.cbFileType, QComboBox)

        self.fileWidget.setStorageMode(QgsFileWidget.GetDirectory)

        formats = [('Portable Network Graphics (*.png)', 'PNG'),
                   ('Joint Photographic Experts Group (*.jpg)', 'JPG'),
                   ('Windows Bitmap (*.bmp)', 'BMP'),
                   ('Portable Bitmap (*.pbm)', 'PBM'),
                   ('Portable Graymap (*.pgm)', 'PGM'),
                   ('Portable Pixmap (*.ppm)', 'PPM'),
                   ('X11 Bitmap (*.xbm)', 'XBM'),
                   ('X11 Pixmap (*.xpm)', 'XPM'),
                   ]



        for t in formats:
            self.cbFileType.addItem(t[0], userData=t[1])

        self.fileWidget.fileChanged.connect(self.validate)

        self.buttonBox.button(QDialogButtonBox.Save).clicked.connect(lambda : self.setResult(QDialog.Accepted))
        self.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect(lambda : self.setResult(QDialog.Rejected))
        self.validate()

    def validate(self, *args):

        b = os.path.isdir(self.directory())
        self.buttonBox.button(QDialogButtonBox.Save).setEnabled(b)


    def setDirectory(self, path:str):
        assert os.path.isdir(path)
        self.fileWidget.setFilePath(path)


    def directory(self)->str:
        """
        Returns the selected directory
        :return: str
        """
        return self.fileWidget.filePath()

    def fileType(self)->str:
        """
        Returns the selected file type
        :return:
        """
        return self.cbFileType.currentData(Qt.UserRole)



def disconnect_signal(signal):
    while True:
        try:
            signal.disconnect()
        except TypeError:
            break