Skip to content

Commit 00dde01

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix_1430_AttributeError-SpectralLibraryDock
2 parents 0cd1c5b + 26c8d64 commit 00dde01

File tree

8 files changed

+137
-82
lines changed

8 files changed

+137
-82
lines changed

enmapbox/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
URL_TESTDATA = 'https://github.com/EnMAP-Box/enmap-box-exampledata/releases/download/v1.1.1/exampledata.zip'
5959
URL_INSTALLATION = r'https://enmap-box.readthedocs.io/en/latest/usr_section/usr_installation.html#install-required-python-packages'
6060
URL_QGIS_RESOURCES = r'https://github.com/EnMAP-Box/qgispluginsupport/releases/download/qgisresources.zip_2025-11-07/qgisresources.zip'
61-
PLUGIN_DEPENDENCIES = ['vrtbuilderplugin>=0.9']
6261

6362
DIR_ENMAPBOX = os.path.dirname(__file__)
6463
DIR_REPO = os.path.dirname(DIR_ENMAPBOX)

enmapbox/coreapps/rasterlayerstylingapp/rasterlayerstylingpanel.py

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
from typing import Optional
33

44
from osgeo import gdal
5+
from qgis.PyQt import uic
6+
from qgis.PyQt.QtWidgets import QDoubleSpinBox, QComboBox, QCheckBox, QToolButton, QLabel, QTabWidget, \
7+
QLineEdit, QTableWidget, QSpinBox
8+
from qgis.core import QgsRasterLayer, QgsSingleBandGrayRenderer, QgsRectangle, QgsMapLayer, \
9+
QgsContrastEnhancement, QgsRasterRenderer, QgsMultiBandColorRenderer, QgsSingleBandPseudoColorRenderer, \
10+
QgsMapLayerProxyModel, QgsRasterDataProvider, QgsRasterShader, QgsProject, QgsRasterTransparency
11+
from qgis.gui import (
12+
QgsDockWidget, QgsMapLayerComboBox, QgsCollapsibleGroupBox, QgsColorRampButton, QgsRangeSlider
13+
)
514

615
from enmapbox.gui.dataviews.dockmanager import DockPanelUI
716
from enmapbox.gui.enmapboxgui import EnMAPBox
@@ -10,18 +19,9 @@
1019
from enmapbox.typeguard import typechecked
1120
from enmapbox.utils import BlockSignals
1221
from enmapboxprocessing.algorithm.createspectralindicesalgorithm import CreateSpectralIndicesAlgorithm
13-
from enmapboxprocessing.rasterreader import RasterReader
22+
from enmapboxprocessing.rasterreader import RasterReader, metadataCache, setMetadataCache, buildMetadataCache
1423
from enmapboxprocessing.rasterwriter import RasterWriter
1524
from enmapboxprocessing.utils import Utils
16-
from qgis.PyQt import uic
17-
from qgis.PyQt.QtWidgets import QDoubleSpinBox, QComboBox, QCheckBox, QToolButton, QLabel, QTabWidget, \
18-
QLineEdit, QTableWidget, QSpinBox
19-
from qgis.core import QgsRasterLayer, QgsSingleBandGrayRenderer, QgsRectangle, QgsMapLayer, \
20-
QgsContrastEnhancement, QgsRasterRenderer, QgsMultiBandColorRenderer, QgsSingleBandPseudoColorRenderer, \
21-
QgsMapLayerProxyModel, QgsRasterDataProvider, QgsRasterShader, QgsProject, QgsRasterTransparency
22-
from qgis.gui import (
23-
QgsDockWidget, QgsMapLayerComboBox, QgsCollapsibleGroupBox, QgsColorRampButton, QgsRangeSlider
24-
)
2525
from rasterlayerstylingapp.rasterlayerstylingbandwidget import RasterLayerStylingBandWidget
2626
from rasterlayerstylingapp.rasterlayerstylingpercentileswidget import RasterLayerStylingPercentilesWidget
2727

@@ -141,6 +141,8 @@ def __init__(self, enmapBox: EnMAPBox, parent=None):
141141
# init GUI
142142
self.mRenderer.setCurrentIndex(self.DefaultRendererTab)
143143

144+
self.initMinMax = 0
145+
144146
def project(self) -> QgsProject:
145147
return self.enmapBox.project()
146148

@@ -291,6 +293,11 @@ def onLayerChanged(self):
291293
self.disableGui()
292294
return
293295

296+
hasNoMetadataCache = metadataCache(layer) is None
297+
if hasNoMetadataCache:
298+
cache = buildMetadataCache(layer)
299+
setMetadataCache(layer, cache)
300+
294301
try:
295302
layer.rendererChanged.disconnect(self.onLayerRendererChanged)
296303
except Exception:
@@ -335,7 +342,9 @@ def onRendererTabChanged(self):
335342
if isinstance(self.originalRenderer, QgsMultiBandColorRenderer):
336343
renderer = self.originalRenderer.clone()
337344
else:
338-
renderer = Utils.multiBandColorRenderer(layer.dataProvider(), [1] * 3, [nan] * 3, [nan] * 3)
345+
renderer = Utils.multiBandColorRenderer(
346+
layer.dataProvider(), [1] * 3, [self.initMinMax] * 3, [self.initMinMax] * 3
347+
)
339348
layer.setRenderer(renderer)
340349

341350
for mBand, ce, bandNo in [
@@ -358,7 +367,7 @@ def onRendererTabChanged(self):
358367
if isinstance(self.originalRenderer, QgsSingleBandGrayRenderer):
359368
renderer = self.originalRenderer.clone()
360369
else:
361-
renderer = Utils.singleBandGrayRenderer(layer.dataProvider(), 1, nan, nan)
370+
renderer = Utils.singleBandGrayRenderer(layer.dataProvider(), 1, self.initMinMax, self.initMinMax)
362371
layer.setRenderer(renderer)
363372
ce: QgsContrastEnhancement = renderer.contrastEnhancement()
364373

@@ -377,13 +386,16 @@ def onRendererTabChanged(self):
377386
if isinstance(self.originalRenderer, QgsSingleBandPseudoColorRenderer):
378387
renderer = self.originalRenderer.clone()
379388
else:
380-
renderer = Utils.singleBandPseudoColorRenderer(layer.dataProvider(), 1, nan, nan, None)
389+
renderer = Utils.singleBandPseudoColorRenderer(
390+
layer.dataProvider(), 1, self.initMinMax, self.initMinMax, None
391+
)
381392
layer.setRenderer(renderer)
382393
shader: QgsRasterShader = renderer.shader()
394+
shaderFunction = shader.rasterShaderFunction()
383395

384396
with BlockSignals(self.mPseudoBand.mMin, self.mPseudoBand.mMax, self.mPseudoBand.mBandNo):
385-
self.mPseudoBand.mMin.setText(str(shader.minimumValue()))
386-
self.mPseudoBand.mMax.setText(str(shader.maximumValue()))
397+
self.mPseudoBand.mMin.setText(str(shaderFunction.minimumValue()))
398+
self.mPseudoBand.mMax.setText(str(shaderFunction.maximumValue()))
387399
self.mPseudoBand.mBandNo.setBand(renderer.inputBand())
388400
self.mPseudoBand.mSlider.setValue(renderer.inputBand())
389401

@@ -427,16 +439,22 @@ def onBandChanged(self):
427439
# If so, create a new renderer with correct type.
428440
if self.mRenderer.currentIndex() == self.RgbRendererTab:
429441
if not isinstance(layer.renderer(), QgsMultiBandColorRenderer):
430-
renderer = Utils.multiBandColorRenderer(layer.dataProvider(), [bandNo] * 3, [nan] * 3, [nan] * 3)
442+
renderer = Utils.multiBandColorRenderer(
443+
layer.dataProvider(), [bandNo] * 3, [self.initMinMax] * 3, [self.initMinMax] * 3
444+
)
431445
layer.setRenderer(renderer)
432446
elif self.mRenderer.currentIndex() == self.GrayRendererTab:
433447
if not isinstance(layer.renderer(), QgsSingleBandGrayRenderer):
434-
renderer = Utils.singleBandGrayRenderer(layer.dataProvider(), bandNo, nan, nan)
448+
renderer = Utils.singleBandGrayRenderer(layer.dataProvider(), bandNo, self.initMinMax, self.initMinMax)
435449
layer.setRenderer(renderer)
436450
elif self.mRenderer.currentIndex() == self.PseudoRendererTab:
437451
if not isinstance(layer.renderer(), QgsSingleBandPseudoColorRenderer):
438-
renderer = Utils.singleBandPseudoColorRenderer(layer.dataProvider(), bandNo, nan, nan, None)
452+
renderer = Utils.singleBandPseudoColorRenderer(
453+
layer.dataProvider(), bandNo, self.initMinMax, self.initMinMax, None
454+
)
439455
layer.setRenderer(renderer)
456+
elif self.mRenderer.currentIndex() == self.DefaultRendererTab:
457+
pass
440458
else:
441459
raise ValueError()
442460

@@ -534,6 +552,8 @@ def onTransparencyRangeChanged(self):
534552
lower, upper = provider.cumulativeCut(
535553
bandNo, max(p1, 0) / 100., min(p2, 100) / 100., layer.extent(), int(QgsRasterLayer.SAMPLE_SIZE)
536554
)
555+
if lower is nan or upper is nan:
556+
lower = upper = 0
537557
if p1 == 0:
538558
self.mTransparencyLower.setText('')
539559
else:
@@ -591,6 +611,8 @@ def setCumulativeCut(bandNo: int, mBandMin: QLineEdit, mBandMax: QLineEdit):
591611
bandNo, self.mP1.value() / 100., self.mP2.value() / 100., extent,
592612
self.currentSampleSize(self.mAccuracy.currentIndex())
593613
)
614+
if vmin is nan or vmax is nan:
615+
vmin = vmax = 0
594616

595617
with BlockSignals(mBandMin, mBandMax):
596618
mBandMin.setText(str(vmin))
@@ -757,6 +779,13 @@ def updateLinkedLayerRenderer(self):
757779
self.currentSampleSize(accuracyType)
758780
)
759781

782+
if redMin is nan or redMax is nan:
783+
redMin = redMax = 0
784+
if greenMin is nan or greenMax is nan:
785+
greenMin = greenMax = 0
786+
if blueMin is nan or blueMax is nan:
787+
blueMin = blueMax = 0
788+
760789
elif stretchType == self.ReferenceLayerStrech:
761790
ce: QgsContrastEnhancement = renderer.redContrastEnhancement()
762791
redMin = ce.minimumValue()
@@ -831,6 +860,8 @@ def updateLinkedLayerRenderer(self):
831860
bandNo, p1 / 100., p2 / 100., self.currentExtent(layer2, statisticsType),
832861
self.currentSampleSize(accuracyType)
833862
)
863+
if redMin is nan or redMax is nan:
864+
redMin = redMax = 0
834865

835866
elif stretchType == self.ReferenceLayerStrech:
836867
ce: QgsContrastEnhancement = renderer.contrastEnhancement()
@@ -871,6 +902,8 @@ def updateLinkedLayerRenderer(self):
871902
bandNo, p1 / 100., p2 / 100., self.currentExtent(layer2, statisticsType),
872903
self.currentSampleSize(accuracyType)
873904
)
905+
if redMin is nan or redMax is nan:
906+
redMin = redMax = 0
874907
elif stretchType == self.ReferenceLayerStrech:
875908
shader: QgsRasterShader = renderer.shader()
876909
redMin = shader.minimumValue()
@@ -953,6 +986,10 @@ def enableGui(self):
953986
# utils
954987
def tofloat(text: str) -> float:
955988
try:
956-
return float(text)
989+
value = float(text)
990+
if value is nan:
991+
value = 0
957992
except Exception:
958-
return nan
993+
value = 0
994+
995+
return value

enmapbox/eo4qapps/geetimeseriesexplorerapp/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,14 @@ def toggleMainDockVisibility(self):
9494
if not isEarthEngineModuleInstalled():
9595
self.mainDock.setVisible(False)
9696
self.profileDock.setVisible(False)
97-
QgsMessageLog.logMessage("Can't import ee (Earth Engine) package.", level=Qgis.MessageLevel.Warning)
97+
title = 'Missing Plugin Dependency'
98+
text = "install Google Earth Engine QGIS Plugin via the Plugin Manager."
99+
QgsMessageLog.logMessage(text, level=Qgis.MessageLevel.Warning)
100+
if isinstance(self.interface, EnMAPBox):
101+
self.interface.messageBar().pushWarning(title, text)
102+
else:
103+
from qgis.utils import iface
104+
iface.messageBar().pushWarning(title, text)
98105
return
99106

100107
self.mainDock.setVisible(not self.mainDock.isVisible())

enmapbox/eo4qapps/profileanalyticsapp/profileanalyticsdockwidget.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,6 @@ def onApplyClicked(self):
432432
'DISTANCE': samplingDistance,
433433
'START_OFFSET': 0,
434434
'END_OFFSET': 0,
435-
# 'OUTPUT': r'C:\Users\Andreas\Downloads\points.gpkg' # 'TEMPORARY_OUTPUT'
436435
'OUTPUT': 'TEMPORARY_OUTPUT'
437436
}
438437
pointLayer = processing.run(alg, parameters)['OUTPUT']
@@ -453,7 +452,6 @@ def onApplyClicked(self):
453452
'RASTERCOPY': rasterLayer,
454453
'COLUMN_PREFIX': 'SAMPLE_',
455454
'OUTPUT': 'TEMPORARY_OUTPUT'
456-
# 'OUTPUT': r'C:\Users\Andreas\Downloads\sample.gpkg' # 'TEMPORARY_OUTPUT'
457455
}
458456
pointLayer2: QgsVectorLayer = processing.run(alg, parameters)['OUTPUT']
459457
xValues = [feature['distance'] for feature in pointLayer2.getFeatures()]
@@ -474,7 +472,6 @@ def onApplyClicked(self):
474472
return
475473

476474
polygonId = polygonLayer.selectedFeatureIds()[0]
477-
name = f'{layer.name()} [band {bandNo}, polygon ID {polygonId}]'
478475

479476
# 1. extract region of interest from raster
480477
alg = 'gdal:cliprasterbymasklayer'
@@ -484,6 +481,7 @@ def onApplyClicked(self):
484481
polygonLayer.source(), selectedFeaturesOnly=True, featureLimit=-1,
485482
geometryCheck=QgsFeatureRequest.InvalidGeometryCheck.GeometryAbortOnInvalid),
486483
'TARGET_CRS': layer.crs(),
484+
"DATA_TYPE": 6,
487485
'NODATA': np.nan,
488486
'OUTPUT': 'TEMPORARY_OUTPUT'
489487
}
@@ -548,7 +546,7 @@ def onApplyClicked(self):
548546
except Exception:
549547
traceback.print_exc()
550548

551-
userFunctionEditor = w.dialog
549+
userFunctionEditor = getattr(w, 'dialog', None)
552550
xValues = [float(v) for v in xValues]
553551
yValues = [float(v) for v in yValues]
554552
profile = Profile(xValues, yValues, xUnit, name, style)
@@ -634,19 +632,20 @@ def onApplyClicked(self):
634632
dialog.mLog.setText(msg)
635633

636634
# add profiles to library (for potential visualization in a Spectral View)
637-
allProfiles = profiles + ufuncProfiles
638-
self.mLibrary.dataProvider().truncate() # delete all features
639-
self.mLibrary.startEditing()
640-
for id, profile in enumerate(allProfiles):
641-
profileValueDict = prepareProfileValueDict(
642-
profile.xValues, profile.yValues, profile.xUnit)
643-
feature = QgsFeature()
644-
feature.setId(id)
645-
feature.setFields(self.mLibrary.fields())
646-
feature.setAttribute('name', profile.name)
647-
feature.setAttribute('profiles', profileValueDict)
648-
self.mLibrary.addFeatures([feature])
649-
self.mLibrary.commitChanges()
635+
if self.mLibrary is not None:
636+
allProfiles = profiles + ufuncProfiles
637+
self.mLibrary.dataProvider().truncate() # delete all features
638+
self.mLibrary.startEditing()
639+
for id, profile in enumerate(allProfiles):
640+
profileValueDict = prepareProfileValueDict(
641+
profile.xValues, profile.yValues, profile.xUnit)
642+
feature = QgsFeature()
643+
feature.setId(id)
644+
feature.setFields(self.mLibrary.fields())
645+
feature.setAttribute('name', profile.name)
646+
feature.setAttribute('profiles', profileValueDict)
647+
self.mLibrary.addFeatures([feature])
648+
self.mLibrary.commitChanges()
650649

651650
# set x axis title
652651
if self.mSourceType.currentIndex() == self.RasterLayerSource:

0 commit comments

Comments
 (0)