Commit be86bd1f authored by Benjamin Jakimow's avatar Benjamin Jakimow
Browse files

added scripts/update_docs.py and updated doc/source

refactoring
parent de4232a3
Pipeline #11107 failed
......@@ -34,5 +34,5 @@ def classFactory(iface): # pylint: disable=invalid-name
if not d in sys.path:
sys.path.append(d)
from eotimeseriesviewer.timeseriesviewerplugin import TimeSeriesViewerPlugin
return TimeSeriesViewerPlugin(iface)
from eotimeseriesviewer.eotimeseriesviewerplugin import EOTimeSeriesViewerPlugin
return EOTimeSeriesViewerPlugin(iface)
==============
Changelog
==============
2020-04-09 (version 1.12):
* TimeSeries tree view allows to change the visibility of single source images, e.g. to hide clouded observations
* several updates to the Spectral Library Widget, e.g. import / export of profiles from ASD, ARTMO, EcoSYS or SPECCHIO
* EOTSV allows to open images from sources with subdatasets, e.g. from Sentinel-2 or HDF images.
2020-01-23 (version 1.11):
* revised unit tests for CI pipelines
* fixed smaller issues in SensorModel
......@@ -51,7 +57,7 @@ Changelog
2019-07-02 (version 1.4):
* adding vector layers with sublayers will add all sublayers
* map canvas context menu "Focus on Spatial Extent" will hide maps without time series data for the current spatial extent
* labeling dock allows to iterate over vector features. the spatial map extent will be centered to each feature (`#26 <https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/26>`_)
* labeling dock allows to iterate over vector features. the spatial map extent will be centered to each feature (#26)
* added several convenience function to TimeSeriesViewer object
* fixed a bug that did not allow to create new polygon features
* temporal profile visualization: fixed icons to preview selected plot style, coordinate described by "<fid> <name>", e.g. "42 Deforested", fixed plot style preview
......@@ -73,7 +79,7 @@ Changelog
* dates and data sources of the TimeSeries are now shown in a TreeView instead TableView
* observation dates of current visible map canvases are highlighted in the time series tree view
* sensor raster layer properties can be opened from MapView layer tree `#87 <https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/87>`_. Stats will be related to center mapcanvas.
* fixed: StackedInputDialog, MapCanvas context menu, "Save Changes?" labeling dialog (`#85 <https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/85>`_), remove temporal profile (`#86 <https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/86>`_), draw new feature error (`#84 <https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/84>`_), Crosshair button status (`#90 <https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/90>`_), and some more
* fixed: StackedInputDialog, MapCanvas context menu, "Save Changes?" labeling dialog (#85), remove temporal profile (#86), draw new feature error (#84), Crosshair button status (#90), and some more
2019-05-15 (version 1.0, major update):
......
......@@ -2,6 +2,25 @@
Visual Changelog
================
Version 1.12
------------
* The TimeSeries tree view allows to change the visibility of single source image
* The new context menu entry *Add Product* offers specialized methods to load source images.
*Add Product > Sentinel-2* is used to load the sub-datasets of interest from Sentinel-2 products,
*Add Product > Subdatasets* generally allows to load subdatasets e.g. from HDF images.
Version 1.11
------------
* this version did not provide major visual changes (with except of less error messages)
Version 1.10
------------
......
......@@ -37,7 +37,6 @@ CREATE_ISSUE = 'https://bitbucket.org/jakimowb/eo-time-series-viewer/issues/new'
DEPENDENCIES = ['numpy', 'gdal']
URL_TESTDATA = r''
import os
import sys
import fnmatch
......@@ -47,10 +46,12 @@ import pathlib
from qgis.core import QgsApplication, Qgis
from qgis.PyQt.QtGui import QIcon
DEBUG: bool = bool(os.environ.get('EOTSV_DEBUG', False))
DIR = pathlib.Path(__file__).parent
DIR_REPO = DIR.parent
DIR_UI = DIR / 'ui'
DIR_DOCS = DIR / 'docs'
DIR_DOCS = DIR_REPO / 'doc'
DIR_EXAMPLES = DIR_REPO / 'example'
PATH_EXAMPLE_TIMESERIES = DIR_EXAMPLES / 'ExampleTimeSeries.csv'
PATH_LICENSE = DIR_REPO / 'LICENSE.md'
......@@ -59,19 +60,38 @@ PATH_ABOUT = DIR_REPO / 'ABOUT.html'
DIR_QGIS_RESOURCES = DIR_REPO / 'qgisresources'
URL_QGIS_RESOURCES = r'https://bitbucket.org/jakimowb/qgispluginsupport/downloads/qgisresources.zip'
def debugLog(msg: str):
if DEBUG:
print('DEBUG:' + msg, flush=True)
# import QPS modules
# skip imports when on RTD, as we can not install the full QGIS environment as required
# https://docs.readthedocs.io/en/stable/builds.html
if not os.environ.get('READTHEDOCS') in ['True', 'TRUE', True]:
debugLog('load crosshair')
from .externals.qps.crosshair.crosshair import CrosshairStyle, CrosshairWidget, CrosshairMapCanvasItem, CrosshairDialog, getCrosshairStyle
debugLog('load plotstyling')
from .externals.qps.plotstyling.plotstyling import PlotStyle, PlotStyleDialog, PlotStyleButton, PlotStyleWidget
debugLog('load classification')
from .externals.qps.classification.classificationscheme import ClassificationScheme, ClassInfo, ClassificationSchemeComboBox, ClassificationSchemeWidget, ClassificationSchemeDialog, hasClassification
debugLog('load models')
from .externals.qps.models import Option, OptionListModel, TreeNode, TreeModel, TreeView
debugLog('load speclib')
from .externals.qps.speclib.core import SpectralLibrary, SpectralProfile
from .externals.qps.speclib.gui import SpectralLibraryPanel, SpectralLibraryWidget
debugLog('load layer config')
from .externals.qps.layerconfigwidgets.vectorlayerfields import LayerFieldsConfigWidget
debugLog('load maptools')
from .externals.qps.maptools import *
debugLog('load utils')
from .externals.qps.utils import *
......@@ -83,21 +103,26 @@ def messageLog(msg, level=Qgis.Info):
"""
QgsApplication.instance().messageLog().logMessage(msg, LOG_MESSAGE_TAG, level)
def initResources():
"""
Loads (or reloads) required Qt resources
:return:
"""
debugLog('initResources')
from eotimeseriesviewer.externals.qps.resources import initQtResources
initQtResources(pathlib.Path(__file__).parent)
def initEditorWidgets():
"""
Initialises QgsEditorWidgets
"""
debugLog('initEditorWidgets')
import eotimeseriesviewer.externals.qps as qps
qps.registerEditorWidgets()
def initAll():
"""
Calls all required init routines
......@@ -106,6 +131,7 @@ def initAll():
initResources()
initEditorWidgets()
def icon() -> QIcon:
"""
Returns the EO Time Series Viewer icon
......
......@@ -20,16 +20,16 @@
"""
# noinspection PyPep8Naming
import os, sys, site
import os
import sys
import site
from qgis.gui import *
from qgis.core import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *
class TimeSeriesViewerPlugin:
class EOTimeSeriesViewerPlugin:
def __init__(self, iface):
......@@ -40,11 +40,13 @@ class TimeSeriesViewerPlugin:
site.addsitedir(dirPlugin)
import eotimeseriesviewer
eotimeseriesviewer.debugLog('initial Dependency Check')
# run a dependency check
self.initialDependencyCheck()
# initialize required settings
eotimeseriesviewer.debugLog('initial all')
eotimeseriesviewer.initAll()
def initGui(self):
......@@ -65,7 +67,6 @@ class TimeSeriesViewerPlugin:
self.iface.addToolBarIcon(action)
self.iface.addPluginToRasterMenu(TITLE, action)
def initialDependencyCheck(self):
"""
Runs a check for availability of package dependencies and give an readible error message
......@@ -100,15 +101,12 @@ class TimeSeriesViewerPlugin:
messageLog(longText)
raise Exception(longText)
def run(self):
from eotimeseriesviewer.main import EOTimeSeriesViewer
self.mEOTSV = EOTimeSeriesViewer()
self.mEOTSV.ui.sigAboutToBeClosed.connect(self.onUiClosed)
self.mEOTSV.show()
def onUiClosed(self):
self.mEOTSV = None
from eotimeseriesviewer.main import EOTimeSeriesViewer
......@@ -122,6 +120,5 @@ class TimeSeriesViewerPlugin:
self.onUiClosed()
def tr(self, message):
return QCoreApplication.translate('EOTimeSeriesViewerPlugin', message)
\ No newline at end of file
......@@ -34,7 +34,7 @@ def unregisterMapLayerConfigWidgetFactory(factory:QgsMapLayerConfigWidgetFactory
while factory in MAPLAYER_CONFIGWIDGET_FACTORIES:
MAPLAYER_CONFIGWIDGET_FACTORIES.remove(factory)
def mapLayerConfigWidgetFactories()->typing.List[QgsMapLayerConfigWidgetFactory]:
def mapLayerConfigWidgetFactories() -> typing.List[QgsMapLayerConfigWidgetFactory]:
"""
Returns registered QgsMapLayerConfigWidgetFactories
:return: list of QgsMapLayerConfigWidgetFactories
......
......@@ -126,7 +126,7 @@ class CrosshairMapCanvasItem(QgsMapCanvasItem):
self.mPosition = point
self.mCanvas.update()
def crosshairStyle(self)->CrosshairStyle:
def crosshairStyle(self) -> CrosshairStyle:
"""
Returns the crosshair style
:return: CrosshairStyle
......@@ -146,7 +146,7 @@ class CrosshairMapCanvasItem(QgsMapCanvasItem):
if old != b:
self.mCanvas.update()
def visibility(self)->bool:
def visibility(self) -> bool:
"""Returns the Crosshair visibility"""
return self.mShow
......@@ -178,7 +178,7 @@ class CrosshairMapCanvasItem(QgsMapCanvasItem):
"""
self.mRasterGridLayer = None
def rasterGridLayer(self)->QgsRasterLayer:
def rasterGridLayer(self) -> QgsRasterLayer:
"""
Returns the raster grid layer
:return: QgsRasterLayer
......@@ -545,7 +545,7 @@ class CrosshairDialog(QgsDialog):
if isinstance(crosshairStyle, CrosshairStyle):
self.setCrosshairStyle(crosshairStyle)
def crosshairStyle(self)->CrosshairStyle:
def crosshairStyle(self) -> CrosshairStyle:
"""
Returns the specfied CrosshairStyle
:return: CrosshairStyle
......
......@@ -130,7 +130,7 @@ class CursorLocationInfoModel(TreeModel):
return
# get-or-create node
def gocn(root, name)->TreeNode:
def gocn(root, name) -> TreeNode:
assert isinstance(root, TreeNode)
n = TreeNode(root, name)
weakId = self.weakNodeId(n)
......@@ -520,7 +520,7 @@ class CursorLocationInfoDock(QDockWidget):
self.btnCrs.setCrs(crs)
self.updateCursorLocationInfo()
def cursorLocation(self)->SpatialPoint:
def cursorLocation(self) -> SpatialPoint:
"""
Returns the last location that was set.
"""
......
......@@ -9,7 +9,7 @@ from qgis.PyQt.QtWidgets import *
from qgis.PyQt.QtGui import QIcon
from ..utils import loadUi
def configWidgetUi(name:str)->str:
def configWidgetUi(name:str) -> str:
"""
Returns the full path to a '*.ui' file
:param name:
......@@ -143,7 +143,7 @@ class SourceConfigWidgetFactory(QgsMapLayerConfigWidgetFactory):
def createWidget(self, layer, canvas, dockWidget=False, parent=None):
return SourceConfigWidget(layer, canvas, parent=parent)
def supportsLayer(self, layer)->bool:
def supportsLayer(self, layer) -> bool:
return isinstance(layer, QgsMapLayer)
def supportLayerPropertiesDialog(self):
......@@ -167,7 +167,7 @@ class SymbologyConfigWidget(QpsMapLayerConfigWidget):
self.syncToLayer()
def symbologyWidget(self)->typing.Union[QgsRendererRasterPropertiesWidget, QgsRendererPropertiesDialog]:
def symbologyWidget(self) -> typing.Union[QgsRendererRasterPropertiesWidget, QgsRendererPropertiesDialog]:
return self.scrollArea.widget()
def menuButtonMenu(self) ->QMenu:
......
......@@ -455,7 +455,7 @@ class GDALMetadataConfigWidgetFactory(QgsMapLayerConfigWidgetFactory):
self.mIsOGR = isinstance(layer, QgsVectorLayer) and layer.dataProvider().name() == 'ogr'
return self.mIsGDAL or self.mIsOGR
def icon(self)->QIcon:
def icon(self) -> QIcon:
if self.mIsGDAL:
return QIcon(self.mIconGDAL)
if self.mIsOGR:
......@@ -468,14 +468,14 @@ class GDALMetadataConfigWidgetFactory(QgsMapLayerConfigWidgetFactory):
def supportsStyleDock(self):
return False
def createWidget(self, layer, canvas, dockWidget=True, parent=None)->GDALMetadataModelConfigWidget:
def createWidget(self, layer, canvas, dockWidget=True, parent=None) -> GDALMetadataModelConfigWidget:
w = GDALMetadataModelConfigWidget(layer, canvas, parent=parent)
w.metadataModel.setIsEditable(True)
w.setWindowTitle(self.title())
w.setWindowIcon(self.icon())
return w
def title(self)->str:
def title(self) -> str:
if self.mIsGDAL:
return 'GDAL Metadata'
if self.mIsOGR:
......
......@@ -11,7 +11,7 @@ from ..utils import loadUi, parseWavelength, convertMetricUnit
class RasterBandConfigWidget(QgsMapLayerConfigWidget):
@staticmethod
def icon()->QIcon:
def icon() -> QIcon:
return QIcon(':/qps/ui/icons/rasterband_select.svg')
def __init__(self, layer:QgsRasterLayer, canvas:QgsMapCanvas, parent:QWidget=None):
......@@ -82,7 +82,7 @@ class RasterBandConfigWidget(QgsMapLayerConfigWidget):
renderer = self.mLayer.renderer()
self.setRenderer(renderer)
def renderer(self)->QgsRasterRenderer:
def renderer(self) -> QgsRasterRenderer:
oldRenderer = self.mLayer.renderer()
newRenderer = None
if isinstance(oldRenderer, QgsSingleBandGrayRenderer):
......@@ -157,7 +157,7 @@ class RasterBandConfigWidget(QgsMapLayerConfigWidget):
def shouldTriggerLayerRepaint(self)->bool:
def shouldTriggerLayerRepaint(self) -> bool:
return True
def apply(self):
......@@ -169,7 +169,7 @@ class RasterBandConfigWidget(QgsMapLayerConfigWidget):
self.mLayer.setRenderer(newRenderer)
self.widgetChanged.emit()
def wlBand(self, wlKey:str)->int:
def wlBand(self, wlKey:str) -> int:
"""
Returns the band number for a wavelength
:param wlKey:
......@@ -223,7 +223,7 @@ class RasterBandConfigWidgetFactory(QgsMapLayerConfigWidgetFactory):
def supportsStyleDock(self):
return True
def createWidget(self, layer, canvas, dockWidget=True, parent=None)->QgsMapLayerConfigWidget:
def createWidget(self, layer, canvas, dockWidget=True, parent=None) -> QgsMapLayerConfigWidget:
w = RasterBandConfigWidget(layer, canvas, parent=parent)
return w
......@@ -69,12 +69,12 @@ class LabelingConfigWidget(QpsMapLayerConfigWidget):
else:
self.setLabeling(None)
def labelingGui(self)->QWidget:
def labelingGui(self) -> QWidget:
return self.stackedWidget.currentWidget()
def labeling(self)->QgsAbstractVectorLayerLabeling:
def labeling(self) -> QgsAbstractVectorLayerLabeling:
page = self.labelingGui()
lyr = self.mapLayer()
if not isinstance(lyr, QgsVectorLayer):
......@@ -114,7 +114,7 @@ class LabelingConfigWidget(QpsMapLayerConfigWidget):
self.comboBox.setCurrentIndex(self.comboBox.findData(mode))
def labeling_single(self)->QgsVectorLayerSimpleLabeling:
def labeling_single(self) -> QgsVectorLayerSimpleLabeling:
p = self.panelSingleLabels
assert isinstance(p, QgsTextFormatPanelWidget)
settings = QgsPalLayerSettings()
......@@ -151,13 +151,13 @@ class LabelingConfigWidget(QpsMapLayerConfigWidget):
s = ""
def labeling_rulebased(self)->QgsRuleBasedLabeling:
def labeling_rulebased(self) -> QgsRuleBasedLabeling:
return None
def set_labeling_rulebased(self, labeling:QgsRuleBasedLabeling):
pass
def labeling_blocking(self)->QgsVectorLayerSimpleLabeling:
def labeling_blocking(self) -> QgsVectorLayerSimpleLabeling:
return None
def set_labeling_blocking(self, labeling:QgsVectorLayerSimpleLabeling):
......
......@@ -4,7 +4,6 @@ from qgis.core import *
from qgis.gui import QgsMapCanvas, QgsMapLayerConfigWidget, QgsRasterBandComboBox
from qgis.gui import *
from qgis.PyQt.QtWidgets import *
from qgis.PyQt import Qt
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import QIcon
......@@ -178,42 +177,42 @@ class LayerAttributeFormConfigEditorWidget(QWidget):
"""
self.mConfigWidget.setConfig(dict(self.mInitialConfig))
def factoryKey(self)->str:
def factoryKey(self) -> str:
"""
Returns the QgsEditorWidgetFactory key, e.g. "CheckBox"
:return: str
"""
return self.mKey
def factoryName(self)->str:
def factoryName(self) -> str:
"""
Returns the QgsEditorWidgetFactory name, e.g. "Checkbox"
:return: str
"""
return self.factory().name()
def config(self)->dict:
def config(self) -> dict:
"""
Returns the config dictionary
:return: dict
"""
return self.mConfigWidget.config()
def configWidget(self)->QgsEditorConfigWidget:
def configWidget(self) -> QgsEditorConfigWidget:
"""
Returns the QgsEditorConfigWidget
:return: QgsEditorConfigWidget
"""
return self.mConfigWidget
def factory(self)->QgsEditorWidgetFactory:
def factory(self) -> QgsEditorWidgetFactory:
"""
Returns the QgsEditorWidgetFactory
:return: QgsEditorWidgetFactory
"""
return self.mFactory
def editorWidgetSetup(self)->QgsEditorWidgetSetup:
def editorWidgetSetup(self) -> QgsEditorWidgetSetup:
"""
Creates a QgsEditorWidgetSetup
:return: QgsEditorWidgetSetup
......@@ -309,7 +308,7 @@ class LayerAttributeFormConfigEditorWidget(QWidget):
break
def changed(self)->bool:
def changed(self) -> bool:
"""
Returns True if the QgsEditorWidgetFactory or its configuration has been changed
:return: bool
......@@ -345,11 +344,11 @@ class LayerAttributeFormConfigEditorWidget(QWidget):
self.setFactory(self.mInitialFactoryKey)
self.currentEditorConfigWidget().setConfig(self.mInitialConf)
def currentFieldConfig(self)->ConfigInfo:
def currentFieldConfig(self) -> ConfigInfo:
i = self.cbWidgetType.currentIndex()
return self.mItemModel.item(i)
def currentEditorConfigWidget(self)->QgsEditorConfigWidget:
def currentEditorConfigWidget(self) -> QgsEditorConfigWidget:
return self.currentFieldConfig().configWidget()
def updateConfigWidget(self, index):
......@@ -479,7 +478,7 @@ class LayerAttributeFormConfigWidgetFactory(QgsMapLayerConfigWidgetFactory):
def createWidget(self, layer, canvas, dockWidget=False, parent=None):
return LayerAttributeFormConfigWidget(layer, canvas, parent=parent)
def supportsLayer(self, layer)->bool:
def supportsLayer(self, layer) -> bool:
return isinstance(layer, QgsVectorLayer)
def supportLayerPropertiesDialog(self):
......@@ -606,7 +605,7 @@ class LayerFieldsConfigWidgetFactory(QgsMapLayerConfigWidgetFactory):
def createWidget(self, layer, canvas, dockWidget=False, parent=None):
return LayerFieldsConfigWidget(layer, canvas, parent=parent)
def supportsLayer(self, layer)->bool:
def supportsLayer(self, layer) -> bool:
return isinstance(layer, QgsVectorLayer)
def supportLayerPropertiesDialog(self):
......
......@@ -282,7 +282,7 @@ class RemoveAttributeDialog(QDialog):
return [f.name() for f in self.fields()]
def openRasterLayerSilent(uri, name, provider)->QgsRasterLayer:
def openRasterLayerSilent(uri, name, provider) -> QgsRasterLayer:
"""
Opens a QgsRasterLayer without asking for its CRS in case it is undefined.
:param uri: path
......@@ -346,7 +346,7 @@ def rendererFromXml(xml):
s =""
return None
def defaultRasterRenderer(layer:QgsRasterLayer, bandIndices:list=None, sampleSize:int=256, readQml:bool=True)->QgsRasterRenderer:
def defaultRasterRenderer(layer:QgsRasterLayer, bandIndices:list=None, sampleSize:int=256, readQml:bool=True) -> QgsRasterRenderer:
"""
Returns a default Raster Renderer.
See https://bitbucket.org/hu-geomatics/enmap-box/issues/166/default-raster-visualization
......@@ -575,7 +575,7 @@ def pasteStyleFromClipboard(layer:QgsMapLayer):
layer.setRenderer(renderer)
layer.triggerRepaint()
def subLayerDefinitions(mapLayer:QgsMapLayer)->typing.List[QgsSublayersDialog.LayerDefinition]:
def subLayerDefinitions(mapLayer:QgsMapLayer) -> typing.List[QgsSublayersDialog.LayerDefinition]:
"""
:param mapLayer:QgsMapLayer
......@@ -624,7 +624,7 @@ def subLayerDefinitions(mapLayer:QgsMapLayer)->typing.List[QgsSublayersDialog.La
return definitions
def subLayers(mapLayer:QgsMapLayer, subLayers:list=None)->typing.List[QgsMapLayer]:
def subLayers(mapLayer:QgsMapLayer, subLayers:list=None) -> typing.List[QgsMapLayer]:
"""
Returns a list of QgsMapLayer instances extracted from the input QgsMapLayer.
Returns the "parent" QgsMapLayer in case no sublayers can be extracted
......@@ -680,7 +680,7 @@ def subLayers(mapLayer:QgsMapLayer, subLayers:list=None)->typing.List[QgsMapLaye
class LayerPropertiesDialog(QgsOptionsDialogBase):
@staticmethod
def defaultFactories()->typing.List[QgsMapLayerConfigWidgetFactory]:
def defaultFactories() -> typing.List[QgsMapLayerConfigWidgetFactory]:
"""
Returns a list of default QgsMapLayerConfigWidgetFactory
"""
......@@ -814,7 +814,7 @@ class LayerPropertiesDialog(QgsOptionsDialogBase):
self.reject()
def currentPage(self)->QWidget:
def currentPage(self) -> QWidget:
return self.mOptionsStackedWidget.currentWidget()
def apply(self):
......@@ -840,16 +840,16 @@ class LayerPropertiesDialog(QgsOptionsDialogBase):
assert isinstance(page, int) and page >= 0 and page < self.mOptionsListWidget.count()
self.mOptionsListWidget.setCurrentRow(page)
def pages(self)->typing.List[QgsMapLayerConfigWidget]:
def pages(self) -> typing.List[QgsMapLayerConfigWidget]:
for i in range(self.mOptionsStackedWidget.count()):
w = self.mOptionsStackedWidget.widget(i)
if isinstance(w, QgsMapLayerConfigWidget):
yield w
def canvas(self)->QgsMapCanvas:
def canvas(self) -> QgsMapCanvas:
return self.mCanvas
def mapLayer(self)->QgsMapLayer:
def mapLayer(self) -> QgsMapLayer: