diff --git a/test/style1.txt b/test/style1.txt new file mode 100644 index 0000000000000000000000000000000000000000..6182a760afdb50d3d4c7e5b15477ce5167364537 --- /dev/null +++ b/test/style1.txt @@ -0,0 +1,21 @@ +<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'> +<qgis version="2.18.15" minimumScale="inf" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0"> + <pipe> + <rasterrenderer opacity="1" alphaBand="-1" classificationMax="1493" classificationMinMaxOrigin="CumulativeCutFullExtentEstimated" band="1" classificationMin="160" type="singlebandpseudocolor"> + <rasterTransparency/> + <rastershader> + <colorrampshader colorRampType="INTERPOLATED" clip="0"> + <item alpha="255" value="160" label="160" color="#d7191c"/> + <item alpha="255" value="493.2" label="493.2" color="#fdae61"/> + <item alpha="255" value="826.5" label="826.5" color="#ffffbf"/> + <item alpha="255" value="1160" label="1160" color="#abdda4"/> + <item alpha="255" value="1493" label="1493" color="#2b83ba"/> + </colorrampshader> + </rastershader> + </rasterrenderer> + <brightnesscontrast brightness="0" contrast="0"/> + <huesaturation colorizeGreen="128" colorizeOn="0" colorizeRed="255" colorizeBlue="128" grayscaleMode="0" saturation="0" colorizeStrength="100"/> + <rasterresampler maxOversampling="2"/> + </pipe> + <blendMode>0</blendMode> +</qgis> diff --git a/test/style2.txt b/test/style2.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6393bc86b100afad4ae578225b56407ef903739 --- /dev/null +++ b/test/style2.txt @@ -0,0 +1,17 @@ +<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'> +<qgis version="2.18.15" minimumScale="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0"> + <pipe> + <rasterrenderer opacity="1" alphaBand="-1" band="1" type="paletted"> + <rasterTransparency/> + <colorPalette> + <paletteEntry value="0" color="#000000" label="unclassified"/> + <paletteEntry value="1" color="#ff0000" label="class1"/> + <paletteEntry value="2" color="#00ff00" label="class2"/> + </colorPalette> + </rasterrenderer> + <brightnesscontrast brightness="0" contrast="0"/> + <huesaturation colorizeGreen="128" colorizeOn="0" colorizeRed="255" colorizeBlue="128" grayscaleMode="0" saturation="0" colorizeStrength="100"/> + <rasterresampler maxOversampling="2"/> + </pipe> + <blendMode>0</blendMode> +</qgis> diff --git a/test/style3.txt b/test/style3.txt new file mode 100644 index 0000000000000000000000000000000000000000..83bb054d04d9fc37a3d8a6b2f3f926e68b5a2ad3 --- /dev/null +++ b/test/style3.txt @@ -0,0 +1,93 @@ +<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'> +<qgis simplifyDrawingTol="1" labelsEnabled="0" simplifyAlgorithm="0" maxScale="0" readOnly="0" version="3.0.2-Girona" minScale="1e+8" simplifyMaxScale="1" simplifyLocal="1" simplifyDrawingHints="1" hasScaleBasedVisibilityFlag="0"> + <renderer-v2 enableorderby="0" type="singleSymbol" symbollevels="0" forceraster="0"> + <symbols> + <symbol alpha="1" type="fill" clip_to_extent="1" name="0"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="168,123,178,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + </symbols> + <rotation/> + <sizescale/> + </renderer-v2> + <customproperties/> + <blendMode>0</blendMode> + <featureBlendMode>0</featureBlendMode> + <layerOpacity>1</layerOpacity> + <fieldConfiguration> + <field name="DN"> + <editWidget type=""> + <config> + <Option/> + </config> + </editWidget> + </field> + <field name="class_name"> + <editWidget type=""> + <config> + <Option/> + </config> + </editWidget> + </field> + </fieldConfiguration> + <aliases> + <alias field="DN" index="0" name=""/> + <alias field="class_name" index="1" name=""/> + </aliases> + <excludeAttributesWMS/> + <excludeAttributesWFS/> + <defaults> + <default expression="" field="DN" applyOnUpdate="0"/> + <default expression="" field="class_name" applyOnUpdate="0"/> + </defaults> + <constraints> + <constraint unique_strength="0" exp_strength="0" field="DN" notnull_strength="0" constraints="0"/> + <constraint unique_strength="0" exp_strength="0" field="class_name" notnull_strength="0" constraints="0"/> + </constraints> + <constraintExpressions> + <constraint field="DN" exp="" desc=""/> + <constraint field="class_name" exp="" desc=""/> + </constraintExpressions> + <attributeactions> + <defaultAction key="Canvas" value="{00000000-0000-0000-0000-000000000000}"/> + </attributeactions> + <attributetableconfig sortExpression="" sortOrder="0" actionWidgetStyle="dropDown"> + <columns/> + </attributetableconfig> + <editform></editform> + <editforminit/> + <editforminitcodesource>0</editforminitcodesource> + <editforminitfilepath></editforminitfilepath> + <editforminitcode><![CDATA[]]></editforminitcode> + <featformsuppress>0</featformsuppress> + <editorlayout>generatedlayout</editorlayout> + <editable/> + <labelOnTop/> + <widgets/> + <conditionalstyles> + <rowstyles/> + <fieldstyles/> + </conditionalstyles> + <expressionfields/> + <previewExpression></previewExpression> + <mapTip></mapTip> + <layerGeometryType>2</layerGeometryType> +</qgis> diff --git a/test/style4.txt b/test/style4.txt new file mode 100644 index 0000000000000000000000000000000000000000..d18e30ec2dbffb6e2af22412cd5805757a56178c --- /dev/null +++ b/test/style4.txt @@ -0,0 +1,257 @@ +<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'> +<qgis simplifyDrawingTol="1" labelsEnabled="0" simplifyAlgorithm="0" maxScale="0" readOnly="0" version="3.0.2-Girona" minScale="1e+8" simplifyMaxScale="1" simplifyLocal="1" simplifyDrawingHints="1" hasScaleBasedVisibilityFlag="0"> + <renderer-v2 attr="class_name" enableorderby="0" type="categorizedSymbol" symbollevels="0" forceraster="0"> + <categories> + <category render="true" value="burnt and tilled pasture" label="burnt and tilled pasture" symbol="0"/> + <category render="true" value="burnt pasture" label="burnt pasture" symbol="1"/> + <category render="true" value="burnt secondary regrowth" label="burnt secondary regrowth" symbol="2"/> + <category render="true" value="tilled pasture" label="tilled pasture" symbol="3"/> + <category render="true" value="" label="" symbol="4"/> + </categories> + <symbols> + <symbol alpha="1" type="fill" clip_to_extent="1" name="0"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="228,86,159,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + <symbol alpha="1" type="fill" clip_to_extent="1" name="1"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="102,210,219,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + <symbol alpha="1" type="fill" clip_to_extent="1" name="2"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="102,53,225,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + <symbol alpha="1" type="fill" clip_to_extent="1" name="3"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="129,201,118,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + <symbol alpha="1" type="fill" clip_to_extent="1" name="4"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="232,165,31,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + </symbols> + <source-symbol> + <symbol alpha="1" type="fill" clip_to_extent="1" name="0"> + <layer pass="0" enabled="1" locked="0" class="SimpleFill"> + <prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/> + <prop v="168,123,178,255" k="color"/> + <prop v="bevel" k="joinstyle"/> + <prop v="0,0" k="offset"/> + <prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/> + <prop v="MM" k="offset_unit"/> + <prop v="35,35,35,255" k="outline_color"/> + <prop v="solid" k="outline_style"/> + <prop v="0.26" k="outline_width"/> + <prop v="MM" k="outline_width_unit"/> + <prop v="solid" k="style"/> + <data_defined_properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </data_defined_properties> + </layer> + </symbol> + </source-symbol> + <colorramp type="randomcolors" name="[source]"/> + <rotation/> + <sizescale/> + </renderer-v2> + <customproperties> + <property key="embeddedWidgets/count" value="0"/> + <property key="variableNames"/> + <property key="variableValues"/> + </customproperties> + <blendMode>0</blendMode> + <featureBlendMode>0</featureBlendMode> + <layerOpacity>1</layerOpacity> + <SingleCategoryDiagramRenderer diagramType="Histogram" attributeLegend="1"> + <DiagramCategory barWidth="5" opacity="1" width="15" enabled="0" penColor="#000000" penAlpha="255" backgroundAlpha="255" backgroundColor="#ffffff" rotationOffset="270" scaleBasedVisibility="0" lineSizeScale="3x:0,0,0,0,0,0" height="15" minimumSize="0" maxScaleDenominator="1e+8" sizeScale="3x:0,0,0,0,0,0" penWidth="0" lineSizeType="MM" sizeType="MM" minScaleDenominator="0" labelPlacementMethod="XHeight" scaleDependency="Area" diagramOrientation="Up"> + <fontProperties style="" description="MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0"/> + </DiagramCategory> + </SingleCategoryDiagramRenderer> + <DiagramLayerSettings linePlacementFlags="18" priority="0" placement="0" zIndex="0" obstacle="0" dist="0" showAll="1"> + <properties> + <Option type="Map"> + <Option value="" type="QString" name="name"/> + <Option name="properties"/> + <Option value="collection" type="QString" name="type"/> + </Option> + </properties> + </DiagramLayerSettings> + <fieldConfiguration> + <field name="DN"> + <editWidget type="TextEdit"> + <config> + <Option/> + </config> + </editWidget> + </field> + <field name="class_name"> + <editWidget type="TextEdit"> + <config> + <Option/> + </config> + </editWidget> + </field> + </fieldConfiguration> + <aliases> + <alias field="DN" index="0" name=""/> + <alias field="class_name" index="1" name=""/> + </aliases> + <excludeAttributesWMS/> + <excludeAttributesWFS/> + <defaults> + <default expression="" field="DN" applyOnUpdate="0"/> + <default expression="" field="class_name" applyOnUpdate="0"/> + </defaults> + <constraints> + <constraint unique_strength="0" exp_strength="0" field="DN" notnull_strength="0" constraints="0"/> + <constraint unique_strength="0" exp_strength="0" field="class_name" notnull_strength="0" constraints="0"/> + </constraints> + <constraintExpressions> + <constraint field="DN" exp="" desc=""/> + <constraint field="class_name" exp="" desc=""/> + </constraintExpressions> + <attributeactions> + <defaultAction key="Canvas" value="{00000000-0000-0000-0000-000000000000}"/> + </attributeactions> + <attributetableconfig sortExpression="" sortOrder="0" actionWidgetStyle="dropDown"> + <columns> + <column hidden="0" type="field" name="DN" width="-1"/> + <column hidden="0" type="field" name="class_name" width="-1"/> + <column hidden="1" type="actions" width="-1"/> + </columns> + </attributetableconfig> + <editform></editform> + <editforminit/> + <editforminitcodesource>0</editforminitcodesource> + <editforminitfilepath></editforminitfilepath> + <editforminitcode><![CDATA[# -*- coding: utf-8 -*- +""" +QGIS forms can have a Python function that is called when the form is +opened. + +Use this function to add extra logic to your forms. + +Enter the name of the function in the "Python Init function" +field. +An example follows: +""" +from qgis.PyQt.QtWidgets import QWidget + +def my_form_open(dialog, layer, feature): + geom = feature.geometry() + control = dialog.findChild(QWidget, "MyLineEdit") +]]></editforminitcode> + <featformsuppress>0</featformsuppress> + <editorlayout>generatedlayout</editorlayout> + <editable> + <field name="DN" editable="1"/> + <field name="class_name" editable="1"/> + </editable> + <labelOnTop> + <field labelOnTop="0" name="DN"/> + <field labelOnTop="0" name="class_name"/> + </labelOnTop> + <widgets/> + <conditionalstyles> + <rowstyles/> + <fieldstyles/> + </conditionalstyles> + <expressionfields/> + <previewExpression>class_name</previewExpression> + <mapTip></mapTip> + <layerGeometryType>2</layerGeometryType> +</qgis> diff --git a/test/test_mapvisualization.py b/test/test_mapvisualization.py index ba4c2cb176024fa07feb28269951d71bc9f74454..220d3ad5b7beac3d4e94383e3506cbeea4fa0dd7 100644 --- a/test/test_mapvisualization.py +++ b/test/test_mapvisualization.py @@ -22,10 +22,50 @@ from timeseriesviewer.utils import initQgisApplication from PyQt5.QtGui import * from PyQt5.QtCore import * import unittest +from timeseriesviewer.utils import * from timeseriesviewer.mapcanvas import * - +from timeseriesviewer.mapvisualization import * +from example.Images import Img_2014_05_07_LC82270652014127LGN00_BOA QGIS_APP = initQgisApplication() +def getChildElements(node): + assert isinstance(node, QDomNode) + childs = node.childNodes() + return [childs.at(i) for i in range(childs.count())] + +def compareXML(element1, element2 ): + + assert isinstance(element1, QDomNode) + assert isinstance(element2, QDomNode) + + tag1 = element1.nodeName() + tag2 = element2.nodeName() + if tag1 != tag2: + return False + + elts1 = getChildElements(element1); + elts2 = getChildElements(element2); + + if len(elts1) != len(elts2): + return False + + if len(elts1) == 0: + + + value1 = element1.nodeValue() + value2 = element2.nodeValue() + + if value1 != value2: + return False + else: + return True + else: + for e1, e2 in zip(elts1, elts2): + if not compareXML(e1, e2): + return False + + return True + class testclassDialogTest(unittest.TestCase): """Test rerources work.""" @@ -45,6 +85,142 @@ class testclassDialogTest(unittest.TestCase): m.show() + def test_bandselection(self): + + lyr = QgsRasterLayer(Img_2014_05_07_LC82270652014127LGN00_BOA) + + wl, wlu = parseWavelength(lyr) + self.assertIsInstance(wl, np.ndarray) + self.assertIsInstance(wlu, str) + self.assertEqual(wlu, 'um') + refWL = [0.49, 0.56, 0.66, 0.84, 1.65, 2.2] + + self.assertEqual(len(wl), len(refWL)) + for wla, wlb in zip(wl, refWL): + self.assertAlmostEqual(wla, wlb) + + self.assertEqual(0, bandClosestToWavelength(lyr, 'B')) + s = "" + + def test_renderer(self): + + styleFiles = file_search(os.path.dirname(__file__), 'style*.txt') + + lyr = QgsRasterLayer(Img_2014_05_07_LC82270652014127LGN00_BOA) + + r0 = lyr.renderer() + xml0 = rendererToXml(r0) + r0b = rendererFromXml(xml0) + self.assertTrue(type(r0), type(r0b)) + + + + rasterRenderer = [QgsMultiBandColorRenderer(r0, 3,2,1, QgsContrastEnhancement(), QgsContrastEnhancement(), QgsContrastEnhancement()), + QgsPalettedRasterRenderer(r0,0, [ + QgsPalettedRasterRenderer.Class(0, QColor('black'), 'class1'), + QgsPalettedRasterRenderer.Class(1, QColor('green'), 'class2'), + ] ), + QgsHillshadeRenderer(r0, 0, 0.0, 100.0), + QgsSingleBandPseudoColorRenderer(r0, 0, QgsRasterShader(0.0, 255.0)), + QgsSingleBandColorDataRenderer(r0, 0), + QgsSingleBandGrayRenderer(r0, 0)] + + vectorRenderer = []#[QgsSingleSymbolRenderer(QgsLineSymbol()), QgsPointDistanceRenderer()] + + for r1 in rasterRenderer + vectorRenderer: + print('Test {}'.format(r1.__class__.__name__)) + xml1 = rendererToXml(r1) + self.assertIsInstance(xml1, QDomDocument) + + + r1b = rendererFromXml(xml1) + self.assertTrue(type(r1), type(r1b)) + + if isinstance(r1, QgsRasterRenderer): + self.assertIsInstance(r1b, QgsRasterRenderer) + elif isinstance(r1, QgsFeatureRenderer): + self.assertIsInstance(r1b, QgsFeatureRenderer) + + xml2 = rendererToXml(r1b) + self.assertIsInstance(xml2, QDomDocument) + self.assertTrue(xml1.toString() == xml2.toString()) + + + rClone = cloneRenderer(r1) + self.assertTrue(type(r1), type(rClone)) + xmlClone = rendererToXml(rClone) + self.assertIsInstance(xmlClone, QDomDocument) + + similar = compareXML(xml1.firstChild(), xml2.firstChild()) + self.assertTrue(similar) + + + + + + + for path in styleFiles: + f = open(path, encoding='utf8') + xml = ''.join(f.readlines()) + f.close() + + renderer = rendererFromXml(xml) + self.assertTrue(renderer != None) + #self.assertTrue(isinstance(renderer, QgsRasterRenderer) or isinstance(renderer, QgsFeatureRenderer), msg='Unable to read style from {}'.format(path)) + + + print('test_renderer done') + + def test_maprendersettings(self): + from example.Images import Img_2014_01_15_LC82270652014015LGN00_BOA + + from timeseriesviewer.timeseries import TimeSeries + TS = TimeSeries() + TS.addFiles([Img_2014_01_15_LC82270652014015LGN00_BOA]) + sensor1 = TS.sensors()[0] + w = MapViewRenderSettings(sensor1) + w.show() + + + lyr = QgsRasterLayer(Img_2014_01_15_LC82270652014015LGN00_BOA) + doc = QDomDocument() + err = '' + lyr.exportNamedStyle(doc, err) + xml0 = doc.toString() + self.assertEqual(err, '') + + xml = rendererToXml(lyr.renderer()) + self.assertIsInstance(xml, QDomDocument) + xml = xml.toString() + self.assertEqual(xml0, xml) + + + r0 = lyr.renderer() + r = w.rasterRenderer() + + self.assertIsInstance(r, QgsMultiBandColorRenderer) + + r2 = QgsSingleBandGrayRenderer(r, 2) + w.setRasterRenderer(r2) + self.assertIsInstance(w.currentRenderWidget(), QgsSingleBandGrayRendererWidget) + r2b = w.rasterRenderer() + self.assertIsInstance(r2b, QgsSingleBandGrayRenderer) + xml2, xml2b = rendererToXml(r2).toString(), rendererToXml(r2b).toString() + #self.assertEqual(xml2, xml2b) + + r3 = QgsSingleBandPseudoColorRenderer(r,0) + r3.setClassificationMin(0) + r3.setClassificationMax(100) + w.setRasterRenderer(r3) + self.assertIsInstance(w.currentRenderWidget(), QgsSingleBandPseudoColorRendererWidget) + r3b = w.rasterRenderer() + self.assertIsInstance(r3b, QgsSingleBandPseudoColorRenderer) + xml3, xml3b = rendererToXml(r3).toString(), rendererToXml(r3b).toString() + #self.assertEqual(xml3, xml3b) + s = "" + + + diff --git a/timeseriesviewer/__init__.py b/timeseriesviewer/__init__.py index c5607b8f9ab730c24ec463a07770058cb4432fd8..275d1d70e9d895a86d6b0810ca9589465f474848 100644 --- a/timeseriesviewer/__init__.py +++ b/timeseriesviewer/__init__.py @@ -107,30 +107,8 @@ def icon(): path = os.path.join(os.path.dirname(__file__), 'icon.png') return QIcon(path) - -def file_search(rootdir, pattern, recursive=False, ignoreCase=False): - assert os.path.isdir(rootdir), "Path is not a directory:{}".format(rootdir) - regType = type(re.compile('.*')) - results = [] - - for root, dirs, files in os.walk(rootdir): - for file in files: - if isinstance(pattern, regType): - if pattern.search(file): - path = os.path.join(root, file) - results.append(path) - - elif (ignoreCase and fnmatch.fnmatch(file.lower(), pattern.lower())) \ - or fnmatch.fnmatch(file, pattern): - - path = os.path.join(root, file) - results.append(path) - if not recursive: - break - pass - - return results - +import timeseriesviewer.utils +file_search = timeseriesviewer.utils.file_search def getFileAndAttributes(file): diff --git a/timeseriesviewer/dummy_gdal.vrt b/timeseriesviewer/dummy_gdal.vrt new file mode 100644 index 0000000000000000000000000000000000000000..68af42a2776e5e5d9e867b47648884881eb61824 --- /dev/null +++ b/timeseriesviewer/dummy_gdal.vrt @@ -0,0 +1,22 @@ +<VRTDataset rasterXSize="1" rasterYSize="1"> + <GeoTransform> 0.0000000000000000e+00, 1.0000000000000000e+00, 0.0000000000000000e+00, 0.0000000000000000e+00, 0.0000000000000000e+00, -1.0000000000000000e+00</GeoTransform> + <VRTRasterBand dataType="Float32" band="1"> + <Metadata> + <MDI key="STATISTICS_MAXIMUM">0</MDI> + <MDI key="STATISTICS_MEAN">0</MDI> + <MDI key="STATISTICS_MINIMUM">0</MDI> + <MDI key="STATISTICS_STDDEV">0</MDI> + </Metadata> + <Description>Band 1</Description> + <Histograms> + <HistItem> + <HistMin>0</HistMin> + <HistMax>0</HistMax> + <BucketCount>1</BucketCount> + <IncludeOutOfRange>0</IncludeOutOfRange> + <Approximate>0</Approximate> + <HistCounts>0</HistCounts> + </HistItem> + </Histograms> + </VRTRasterBand> +</VRTDataset> diff --git a/timeseriesviewer/dummy_ogr.vrt b/timeseriesviewer/dummy_ogr.vrt new file mode 100644 index 0000000000000000000000000000000000000000..936190ccb456719625d3258a6a7d9578221457eb --- /dev/null +++ b/timeseriesviewer/dummy_ogr.vrt @@ -0,0 +1,4 @@ +<OGRVRTDataSource> + <OGRVRTLayer name="dummy_layer1"> + </OGRVRTLayer> +</OGRVRTDataSource> \ No newline at end of file diff --git a/timeseriesviewer/mapcanvas.py b/timeseriesviewer/mapcanvas.py index ee2e2fe54d179148dbe090a923dbd6ea2ea33202..3b9f404864e1fd2acf3991e94712b48cc465f409 100644 --- a/timeseriesviewer/mapcanvas.py +++ b/timeseriesviewer/mapcanvas.py @@ -277,7 +277,9 @@ class MapLayerInfo(object): self.mProvider == 'gdal' and isinstance(renderer, QgsRasterRenderer): self.mRenderer = renderer if self.isInitialized(): + from timeseriesviewer.mapvisualization import cloneRenderer copyRenderer(self.mRenderer, self.mLayer) + #self.mLayer.repaintRequested.emit() def setIsVisible(self, b): @@ -852,14 +854,27 @@ class MapCanvas(QgsMapCanvas): if isinstance(r, QgsMultiBandColorRenderer): + #newRenderer = QgsMultiBandColorRenderer(None, r.redBand(), r.greenBand(), r.blueBand()) + newRenderer = cloneRenderer(r) + + ceR = getCE(r.redBand()) + ceG = getCE(r.greenBand()) + ceB = getCE(r.blueBand()) - newRenderer = QgsMultiBandColorRenderer(None, r.redBand(), r.greenBand(), r.blueBand()) - newRenderer.setRedContrastEnhancement(getCE(r.redBand())) - newRenderer.setGreenContrastEnhancement(getCE(r.greenBand())) - newRenderer.setBlueContrastEnhancement(getCE(r.blueBand())) + if False: + vMin = min(ceR.minimumValue(), ceG.minimumValue(), ceB.minimumValue()) + vMax = max(ceR.maximumValue(), ceG.maximumValue(), ceB.maximumValue()) + for ce in [ceR, ceG, ceB]: + assert isinstance(ce, QgsContrastEnhancement) + ce.setMaximumValue(vMax) + ce.setMinimumValue(vMin) + + newRenderer.setRedContrastEnhancement(ceR) + newRenderer.setGreenContrastEnhancement(ceG) + newRenderer.setBlueContrastEnhancement(ceB) elif isinstance(r, QgsSingleBandPseudoColorRenderer): - newRenderer = r.clone() + newRenderer = cloneRenderer(r) ce = getCE(newRenderer.band()) #stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500) @@ -868,7 +883,12 @@ class MapCanvas(QgsMapCanvas): newRenderer.setClassificationMin(ce.minimumValue()) shader.setMaximumValue(ce.maximumValue()) shader.setMinimumValue(ce.minimumValue()) + elif isinstance(r, QgsSingleBandGrayRenderer): + newRenderer = cloneRenderer(r) + s = "" + elif isinstance(r, QgsPalettedRasterRenderer): s = "" + #newRenderer = cloneRenderer(r) if newRenderer is not None: self.sigChangeSVRequest.emit(self, newRenderer) diff --git a/timeseriesviewer/mapvisualization.py b/timeseriesviewer/mapvisualization.py index 5cbc9c3092f62a3343ddc3c2cddd4558f318a773..4a977b75edb99295e309d45d2088fb498289cb7c 100644 --- a/timeseriesviewer/mapvisualization.py +++ b/timeseriesviewer/mapvisualization.py @@ -22,8 +22,6 @@ import os, sys, re, fnmatch, collections, copy, traceback, bisect -import logging -logger = logging.getLogger(__name__) from qgis.core import * from qgis.core import QgsContrastEnhancement, QgsRasterShader, QgsColorRampShader, QgsProject, QgsCoordinateReferenceSystem, \ QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsMapLayerProxyModel, QgsColorRamp, QgsSingleBandPseudoColorRenderer @@ -43,6 +41,15 @@ from timeseriesviewer.mapcanvas import MapCanvas from timeseriesviewer.crosshair import CrosshairStyle + +#dummyPath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'dummy_gdal.vrt')) +#assert os.path.isfile(dummyPath) +#lyr = QgsRasterLayer(dummyPath) +#assert lyr.isValid() +DUMMY_RASTERINTERFACE = QgsSingleBandGrayRenderer(None, 0) + + + class MapViewUI(QFrame, loadUI('mapviewdefinition.ui')): @@ -78,14 +85,18 @@ class MapViewUI(QFrame, loadUI('mapviewdefinition.ui')): def addSensor(self, sensor): assert isinstance(sensor, SensorInstrument) - w = MapViewSensorSettings(sensor) + #w = MapViewSensorSettings(sensor) + w = MapViewRenderSettings(sensor) #sizePolicy = QSizePolicy(QSize) #w.ui. - l = self.renderSettingsLayout + #l = self.renderSettingsLayout + l = self.gbRasterRendering.layout() assert sensor not in self.mSensors.keys() - lastWidgetIndex = l.count()-1 - l.insertWidget(lastWidgetIndex, w.ui) + i = l.count()-1 + while i > 0 and not isinstance(l.itemAt(i), QWidget): + i -= 1 + l.insertWidget(i, w, stretch=0, alignment=Qt.AlignTop) self.mSensors[sensor] = w #self.resize(self.sizeHint()) @@ -96,14 +107,889 @@ class MapViewUI(QFrame, loadUI('mapviewdefinition.ui')): assert isinstance(sensor, SensorInstrument) sensorSettings = self.mSensors.pop(sensor) - assert isinstance(sensorSettings, MapViewSensorSettings) + assert isinstance(sensorSettings, MapViewRenderSettings) - l = self.renderSettingsLayout + #l = self.renderSettingsLayout + l = self.gbRasterRendering.layout() l.removeWidget(sensorSettings.ui) sensorSettings.ui.close() #self.resize(self.sizeHint()) + +class RendererWidgetModifications(object): + + + def __init__(self): + self.mBandComboBoxes = [] + + def modifyGridLayout(self): + gridLayoutOld = self.layout().children()[0] + self.gridLayout = QGridLayout() + while gridLayoutOld.count() > 0: + w = gridLayoutOld.takeAt(0) + w = w.widget() + gridLayoutOld.removeWidget(w) + w.setVisible(False) + setattr(self, w.objectName(), w) + self.layout().removeItem(gridLayoutOld) + self.layout().insertItem(0, self.gridLayout) + self.gridLayout.setSpacing(2) + self.layout().addStretch() + + def connectSliderWithBandComboBox(self, slider, combobox): + """ + Connects a band-selection slider with a band-selection combobox + :param widget: QgsRasterRendererWidget + :param slider: QSlider to show the band number + :param combobox: QComboBox to show the band name + :return: + """ + assert isinstance(self, QgsRasterRendererWidget) + assert isinstance(slider, QSlider) + assert isinstance(combobox, QComboBox) + + # init the slider + nb = self.rasterLayer().dataProvider().bandCount() + slider.setTickPosition(QSlider.TicksAbove) + slider.valueChanged.connect(combobox.setCurrentIndex) + slider.setMinimum(1) + slider.setMaximum(nb) + intervals = [1, 2, 5, 10, 25, 50] + for interval in intervals: + if nb / interval < 10: + break + slider.setTickInterval(interval) + slider.setPageStep(interval) + + def onBandValueChanged(self, idx, slider): + assert isinstance(self, QgsRasterRendererWidget) + assert isinstance(idx, int) + assert isinstance(slider, QSlider) + + # i = slider.value() + slider.blockSignals(True) + slider.setValue(idx) + slider.blockSignals(False) + + # self.minMaxWidget().setBands(myBands) + # self.widgetChanged.emit() + + if self.comboBoxWithNotSetItem(combobox): + combobox.currentIndexChanged[int].connect(lambda idx: onBandValueChanged(self, idx, slider)) + else: + combobox.currentIndexChanged[int].connect(lambda idx: onBandValueChanged(self, idx + 1, slider)) + + def comboBoxWithNotSetItem(self, cb): + assert isinstance(cb, QComboBox) + return cb.itemData(0, role=Qt.DisplayRole) == 'not set' + + def setLayoutItemVisibility(self, grid, isVisible): + assert isinstance(self, QgsRasterRendererWidget) + for i in range(grid.count()): + item = grid.itemAt(i) + if isinstance(item, QLayout): + s = "" + elif isinstance(item, QWidgetItem): + item.widget().setVisible(isVisible) + item.widget().setParent(self) + else: + s = "" + + + + + +def displayBandNames(provider_or_dataset, bands=None): + results = None + if isinstance(provider_or_dataset, QgsRasterLayer): + return displayBandNames(provider_or_dataset.dataProvider()) + elif isinstance(provider_or_dataset, QgsRasterDataProvider): + if provider_or_dataset.name() == 'gdal': + ds = gdal.Open(provider_or_dataset.dataSourceUri()) + results = displayBandNames(ds, bands=bands) + else: + # same as in QgsRasterRendererWidget::displayBandName + results = [] + if bands is None: + bands = range(1, provider_or_dataset.bandCount() + 1) + for band in bands: + result = provider_or_dataset.generateBandName(band) + colorInterp ='{}'.format(provider_or_dataset.colorInterpretationName(band)) + if colorInterp != 'Undefined': + result += '({})'.format(colorInterp) + results.append(result) + + elif isinstance(provider_or_dataset, gdal.Dataset): + results = [] + if bands is None: + bands = range(1, provider_or_dataset.RasterCount+1) + for band in bands: + b = provider_or_dataset.GetRasterBand(band) + descr = b.GetDescription() + if len(descr) == 0: + descr = 'Band {}'.format(band) + results.append(descr) + + return results + +class SingleBandGrayRendererWidget(QgsSingleBandGrayRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return SingleBandGrayRendererWidget(layer, extent) + + def __init__(self, layer, extent): + super(SingleBandGrayRendererWidget, self).__init__(layer, extent) + + self.modifyGridLayout() + self.mGrayBandSlider = QSlider(Qt.Horizontal) + self.mBandComboBoxes.append(self.mGrayBandComboBox) + self.fixBandNames(self.mGrayBandComboBox) + self.connectSliderWithBandComboBox(self.mGrayBandSlider, self.mGrayBandComboBox) + + self.mBtnBar = QFrame() + self.initActionButtons() + + self.gridLayout.addWidget(self.mGrayBandLabel, 0, 0) + self.gridLayout.addWidget(self.mBtnBar, 0, 1, 1, 4, Qt.AlignLeft) + + self.gridLayout.addWidget(self.mGrayBandSlider, 1, 1, 1, 2) + self.gridLayout.addWidget(self.mGrayBandComboBox, 1, 3,1,2) + + self.gridLayout.addWidget(self.label, 2, 0) + self.gridLayout.addWidget(self.mGradientComboBox, 2, 1, 1, 4) + + self.gridLayout.addWidget(self.mMinLabel, 3, 1) + self.gridLayout.addWidget(self.mMinLineEdit, 3, 2) + self.gridLayout.addWidget(self.mMaxLabel, 3, 3) + self.gridLayout.addWidget(self.mMaxLineEdit, 3, 4) + + self.gridLayout.addWidget(self.mContrastEnhancementLabel, 4, 0) + self.gridLayout.addWidget(self.mContrastEnhancementComboBox, 4, 1, 1 ,4) + self.gridLayout.setSpacing(2) + + self.setLayoutItemVisibility(self.gridLayout, True) + + self.mDefaultRenderer = layer.renderer() + + + def initActionButtons(self): + wl, wlu = parseWavelength(self.rasterLayer()) + self.wavelengths = wl + self.wavelengthUnit = wlu + + self.mBtnBar.setLayout(QHBoxLayout()) + self.mBtnBar.layout().addStretch() + self.mBtnBar.layout().setContentsMargins(0, 0, 0, 0) + self.mBtnBar.layout().setSpacing(2) + + self.actionSetDefault = QAction('Default') + self.actionSetRed = QAction('R') + self.actionSetGreen = QAction('G') + self.actionSetBlue = QAction('B') + self.actionSetNIR = QAction('nIR') + self.actionSetSWIR = QAction('swIR') + + self.actionSetDefault.triggered.connect(lambda: self.setBandSelection('default')) + self.actionSetRed.triggered.connect(lambda: self.setBandSelection('R')) + self.actionSetGreen.triggered.connect(lambda: self.setBandSelection('G')) + self.actionSetBlue.triggered.connect(lambda: self.setBandSelection('B')) + self.actionSetNIR.triggered.connect(lambda: self.setBandSelection('nIR')) + self.actionSetSWIR.triggered.connect(lambda: self.setBandSelection('swIR')) + + + def addBtnAction(action): + btn = QToolButton() + btn.setDefaultAction(action) + self.mBtnBar.layout().addWidget(btn) + self.insertAction(None, action) + return btn + + self.btnDefault = addBtnAction(self.actionSetDefault) + self.btnRed = addBtnAction(self.actionSetRed) + self.btnGreen = addBtnAction(self.actionSetGreen) + self.btnBlue = addBtnAction(self.actionSetRed) + self.btnNIR = addBtnAction(self.actionSetNIR) + self.btnSWIR = addBtnAction(self.actionSetSWIR) + + b = self.wavelengths is not None + for a in [self.actionSetRed, self.actionSetGreen, self.actionSetBlue, self.actionSetNIR, self.actionSetSWIR]: + a.setEnabled(b) + + + +class SingleBandPseudoColorRendererWidget(QgsSingleBandPseudoColorRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return SingleBandPseudoColorRendererWidget(layer, extent) + + def __init__(self, layer, extent): + super(SingleBandPseudoColorRendererWidget, self).__init__(layer, extent) + + self.gridLayout = self.layout() + assert isinstance(self.gridLayout, QGridLayout) + for i in range(self.gridLayout.count()): + w = self.gridLayout.itemAt(i) + w = w.widget() + if isinstance(w, QWidget): + setattr(self, w.objectName(), w) + + toReplace = [self.mBandComboBox,self.mMinLabel,self.mMaxLabel, self.mMinLineEdit, self.mMaxLineEdit ] + for w in toReplace: + self.gridLayout.removeWidget(w) + w.setVisible(False) + self.mBandSlider = QSlider(Qt.Horizontal) + self.mBandComboBoxes.append(self.mBandComboBox) + self.fixBandNames(self.mBandComboBox) + self.connectSliderWithBandComboBox(self.mBandSlider, self.mBandComboBox) + + self.mBtnBar = QFrame() + self.initActionButtons() + grid = QGridLayout() + grid.addWidget(self.mBtnBar,0,0,1,4, Qt.AlignLeft) + grid.addWidget(self.mBandSlider, 1,0, 1,2) + grid.addWidget(self.mBandComboBox, 1,2, 1,2) + grid.addWidget(self.mMinLabel, 2, 0) + grid.addWidget(self.mMinLineEdit, 2, 1) + grid.addWidget(self.mMaxLabel, 2, 2) + grid.addWidget(self.mMaxLineEdit, 2, 3) + #grid.setContentsMargins(2, 2, 2, 2, ) + grid.setColumnStretch(0, 0) + grid.setColumnStretch(1, 2) + grid.setColumnStretch(2, 0) + grid.setColumnStretch(3, 2) + grid.setSpacing(2) + self.gridLayout.addItem(grid, 0,1,2,4) + self.gridLayout.setSpacing(2) + self.setLayoutItemVisibility(grid, True) + + + def initActionButtons(self): + from enmapbox.gui.utils import parseWavelength + wl, wlu = parseWavelength(self.rasterLayer()) + self.wavelengths = wl + self.wavelengthUnit = wlu + + self.mBtnBar.setLayout(QHBoxLayout()) + self.mBtnBar.layout().addStretch() + self.mBtnBar.layout().setContentsMargins(0, 0, 0, 0) + self.mBtnBar.layout().setSpacing(2) + + self.actionSetDefault = QAction('Default') + self.actionSetRed = QAction('R') + self.actionSetGreen = QAction('G') + self.actionSetBlue = QAction('B') + self.actionSetNIR = QAction('nIR') + self.actionSetSWIR = QAction('swIR') + + self.actionSetDefault.triggered.connect(lambda: self.setBandSelection('default')) + self.actionSetRed.triggered.connect(lambda: self.setBandSelection('R')) + self.actionSetGreen.triggered.connect(lambda: self.setBandSelection('G')) + self.actionSetBlue.triggered.connect(lambda: self.setBandSelection('B')) + self.actionSetNIR.triggered.connect(lambda: self.setBandSelection('nIR')) + self.actionSetSWIR.triggered.connect(lambda: self.setBandSelection('swIR')) + + + def addBtnAction(action): + btn = QToolButton() + btn.setDefaultAction(action) + self.mBtnBar.layout().addWidget(btn) + self.insertAction(None, action) + return btn + + self.btnDefault = addBtnAction(self.actionSetDefault) + self.btnRed = addBtnAction(self.actionSetRed) + self.btnGreen = addBtnAction(self.actionSetGreen) + self.btnBlue = addBtnAction(self.actionSetRed) + self.btnNIR = addBtnAction(self.actionSetNIR) + self.btnSWIR = addBtnAction(self.actionSetSWIR) + + b = self.wavelengths is not None + for a in [self.actionSetRed, self.actionSetGreen, self.actionSetBlue, self.actionSetNIR, self.actionSetSWIR]: + a.setEnabled(b) + + + + +class MultiBandColorRendererWidget(QgsMultiBandColorRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return MultiBandColorRendererWidget(layer, extent) + + def __init__(self, layer, extent): + super(MultiBandColorRendererWidget, self).__init__(layer, extent) + + self.modifyGridLayout() + + self.mRedBandSlider = QSlider(Qt.Horizontal) + self.mGreenBandSlider = QSlider(Qt.Horizontal) + self.mBlueBandSlider = QSlider(Qt.Horizontal) + + self.mBandComboBoxes.extend([self.mRedBandComboBox, self.mGreenBandComboBox, self.mBlueBandComboBox]) + self.mSliders = [self.mRedBandSlider, self.mGreenBandSlider, self.mBlueBandSlider] + nb = self.rasterLayer().dataProvider().bandCount() + for cbox, slider in zip(self.mBandComboBoxes, self.mSliders): + self.connectSliderWithBandComboBox(slider, cbox) + + + self.fixBandNames(self.mRedBandComboBox) + self.fixBandNames(self.mGreenBandComboBox) + self.fixBandNames(self.mBlueBandComboBox) + + self.mBtnBar = QFrame() + self.mBtnBar.setLayout(QHBoxLayout()) + self.initActionButtons() + self.mBtnBar.layout().addStretch() + self.mBtnBar.layout().setContentsMargins(0, 0, 0, 0) + self.mBtnBar.layout().setSpacing(2) + + #self.gridLayout.deleteLater() +# self.gridLayout = newGrid + self.gridLayout.addWidget(self.mBtnBar, 0, 1, 1, 3) + self.gridLayout.addWidget(self.mRedBandLabel, 1, 0) + self.gridLayout.addWidget(self.mRedBandSlider, 1, 1) + self.gridLayout.addWidget(self.mRedBandComboBox, 1, 2) + self.gridLayout.addWidget(self.mRedMinLineEdit, 1, 3) + self.gridLayout.addWidget(self.mRedMaxLineEdit, 1, 4) + + self.gridLayout.addWidget(self.mGreenBandLabel, 2, 0) + self.gridLayout.addWidget(self.mGreenBandSlider, 2, 1) + self.gridLayout.addWidget(self.mGreenBandComboBox, 2, 2) + self.gridLayout.addWidget(self.mGreenMinLineEdit, 2, 3) + self.gridLayout.addWidget(self.mGreenMaxLineEdit, 2, 4) + + self.gridLayout.addWidget(self.mBlueBandLabel, 3, 0) + self.gridLayout.addWidget(self.mBlueBandSlider, 3, 1) + self.gridLayout.addWidget(self.mBlueBandComboBox, 3, 2) + self.gridLayout.addWidget(self.mBlueMinLineEdit, 3, 3) + self.gridLayout.addWidget(self.mBlueMaxLineEdit, 3, 4) + + self.gridLayout.addWidget(self.mContrastEnhancementAlgorithmLabel, 4, 0, 1, 2) + self.gridLayout.addWidget(self.mContrastEnhancementAlgorithmComboBox, 4, 2, 1, 3) + + self.setLayoutItemVisibility(self.gridLayout, True) + + + self.mRedBandLabel.setText('R') + self.mGreenBandLabel.setText('G') + self.mBlueBandLabel.setText('B') + + self.mDefaultRenderer = layer.renderer() + + + + def initActionButtons(self): + + from enmapbox.gui.utils import parseWavelength + wl, wlu = parseWavelength(self.rasterLayer()) + self.wavelengths = wl + self.wavelengthUnit = wlu + + self.actionSetDefault = QAction('Default') + self.actionSetTrueColor = QAction('RGB') + self.actionSetCIR = QAction('nIR') + self.actionSet453 = QAction('swIR') + + self.actionSetDefault.triggered.connect(lambda: self.setBandSelection('default')) + self.actionSetTrueColor.triggered.connect(lambda: self.setBandSelection('R,G,B')) + self.actionSetCIR.triggered.connect(lambda: self.setBandSelection('nIR,R,G')) + self.actionSet453.triggered.connect(lambda: self.setBandSelection('nIR,swIR,R')) + + + def addBtnAction(action): + btn = QToolButton() + btn.setDefaultAction(action) + self.mBtnBar.layout().addWidget(btn) + self.insertAction(None, action) + return btn + + self.btnDefault = addBtnAction(self.actionSetDefault) + self.btnTrueColor = addBtnAction(self.actionSetTrueColor) + self.btnCIR = addBtnAction(self.actionSetCIR) + self.btn453 = addBtnAction(self.actionSet453) + + b = self.wavelengths is not None + for a in [self.actionSetCIR, self.actionSet453, self.actionSetTrueColor]: + a.setEnabled(b) + +class RendererWidgetModifications(object): + + def __init__(self, *args): + self.initWidgetNames() + self.mBandComboBoxes = [] + + + def modifyGridLayout(self): + + gridLayoutOld = self.layout().children()[0] + self.gridLayout = QGridLayout() + while gridLayoutOld.count() > 0: + w = gridLayoutOld.takeAt(0) + w = w.widget() + gridLayoutOld.removeWidget(w) + w.setVisible(False) + + self.gridLayout.setSpacing(2) + + l = self.layout() + l.removeItem(gridLayoutOld) + if isinstance(l, QBoxLayout): + l.insertItem(0, self.gridLayout) + self.layout().addStretch() + elif isinstance(l, QGridLayout): + l.addItem(self.gridLayout, 0, 0) + + + minMaxWidget = self.minMaxWidget() + if isinstance(minMaxWidget, QWidget): + minMaxWidget.layout().itemAt(0).widget().collapsedStateChanged.connect(self.onCollapsed) + + + def initWidgetNames(self): + for c in self.children(): + setattr(self, c.objectName(), c) + + def onCollapsed(self, b): + hint = self.sizeHint() + self.parent().adjustSize() + # self.parent().setFixedSize(hint) + self.parent().parent().adjustSize() + + def connectSliderWithBandComboBox(self, slider, combobox): + """ + Connects a band-selection slider with a band-selection combobox + :param widget: QgsRasterRendererWidget + :param slider: QSlider to show the band number + :param combobox: QComboBox to show the band name + :return: + """ + assert isinstance(self, QgsRasterRendererWidget) + assert isinstance(slider, QSlider) + assert isinstance(combobox, QComboBox) + + # init the slider + nb = self.rasterLayer().dataProvider().bandCount() + slider.setTickPosition(QSlider.TicksAbove) + slider.valueChanged.connect(combobox.setCurrentIndex) + slider.setMinimum(1) + slider.setMaximum(nb) + intervals = [1, 2, 5, 10, 25, 50] + for interval in intervals: + if nb / interval < 10: + break + slider.setTickInterval(interval) + slider.setPageStep(interval) + + def onBandValueChanged(self, idx, slider): + assert isinstance(self, QgsRasterRendererWidget) + assert isinstance(idx, int) + assert isinstance(slider, QSlider) + + # i = slider.value() + slider.blockSignals(True) + slider.setValue(idx) + slider.blockSignals(False) + + # self.minMaxWidget().setBands(myBands) + # self.widgetChanged.emit() + + if self.comboBoxWithNotSetItem(combobox): + combobox.currentIndexChanged[int].connect(lambda idx: onBandValueChanged(self, idx, slider)) + else: + combobox.currentIndexChanged[int].connect(lambda idx: onBandValueChanged(self, idx + 1, slider)) + + s = "" + + def comboBoxWithNotSetItem(self, cb): + assert isinstance(cb, QComboBox) + return cb.itemData(0, role=Qt.DisplayRole).lower() == 'not set' + + def setLayoutItemVisibility(self, grid, isVisible): + assert isinstance(self, QgsRasterRendererWidget) + for i in range(grid.count()): + item = grid.itemAt(i) + if isinstance(item, QLayout): + s = "" + elif isinstance(item, QWidgetItem): + item.widget().setVisible(isVisible) + item.widget().setParent(self) + else: + s = "" + + def setBandSelection(self, key): + key = key.upper() + if key == 'DEFAULT': + bandIndices = defaultBands(self.rasterLayer()) + else: + colors = re.split('[ ,;:]', key) + + bandIndices = [bandClosestToWavelength(self.rasterLayer(), c) for c in colors] + + n = min(len(bandIndices), len(self.mBandComboBoxes)) + for i in range(n): + cb = self.mBandComboBoxes[i] + bandIndex = bandIndices[i] + if self.comboBoxWithNotSetItem(cb): + cb.setCurrentIndex(bandIndex+1) + else: + cb.setCurrentIndex(bandIndex) + + + def fixBandNames(self, comboBox): + """ + Changes the QGIS default bandnames ("Band 001") to more meaningfull information including gdal.Dataset.Descriptions. + :param widget: + :param comboBox: + """ + nb = self.rasterLayer().bandCount() + + assert isinstance(self, QgsRasterRendererWidget) + assert isinstance(comboBox, QComboBox) + #comboBox.clear() + m = comboBox.model() + assert isinstance(m, QStandardItemModel) + bandNames = displayBandNames(self.rasterLayer()) + + + b = 1 if nb < comboBox.count() else 0 + for i in range(nb): + item = m.item(i+b,0) + assert isinstance(item, QStandardItem) + item.setData(bandNames[i], Qt.DisplayRole) + item.setData('Band {} "{}"'.format(i+1, bandNames[i]), Qt.ToolTipRole) + + + +def displayBandNames(provider_or_dataset, bands=None): + results = None + if isinstance(provider_or_dataset, QgsRasterLayer): + return displayBandNames(provider_or_dataset.dataProvider()) + elif isinstance(provider_or_dataset, QgsRasterDataProvider): + if provider_or_dataset.name() == 'gdal': + ds = gdal.Open(provider_or_dataset.dataSourceUri()) + results = displayBandNames(ds, bands=bands) + else: + # same as in QgsRasterRendererWidget::displayBandName + results = [] + if bands is None: + bands = range(1, provider_or_dataset.bandCount() + 1) + for band in bands: + result = provider_or_dataset.generateBandName(band) + colorInterp ='{}'.format(provider_or_dataset.colorInterpretationName(band)) + if colorInterp != 'Undefined': + result += '({})'.format(colorInterp) + results.append(result) + + elif isinstance(provider_or_dataset, gdal.Dataset): + results = [] + if bands is None: + bands = range(1, provider_or_dataset.RasterCount+1) + for band in bands: + b = provider_or_dataset.GetRasterBand(band) + descr = b.GetDescription() + if len(descr) == 0: + descr = 'Band {}'.format(band) + results.append(descr) + + return results + +class SingleBandGrayRendererWidget(QgsSingleBandGrayRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return SingleBandGrayRendererWidget(layer, extent) + + def __init__(self, layer, extent): + super(SingleBandGrayRendererWidget, self).__init__(layer, extent) + + self.modifyGridLayout() + self.mGrayBandSlider = QSlider(Qt.Horizontal) + self.mBandComboBoxes.append(self.mGrayBandComboBox) + self.fixBandNames(self.mGrayBandComboBox) + self.connectSliderWithBandComboBox(self.mGrayBandSlider, self.mGrayBandComboBox) + + self.mBtnBar = QFrame() + self.initActionButtons() + + self.gridLayout.addWidget(self.mGrayBandLabel, 0, 0) + self.gridLayout.addWidget(self.mBtnBar, 0, 1, 1, 4, Qt.AlignLeft) + + self.gridLayout.addWidget(self.mGrayBandSlider, 1, 1, 1, 2) + self.gridLayout.addWidget(self.mGrayBandComboBox, 1, 3,1,2) + + self.gridLayout.addWidget(self.label, 2, 0) + self.gridLayout.addWidget(self.mGradientComboBox, 2, 1, 1, 4) + + self.gridLayout.addWidget(self.mMinLabel, 3, 1) + self.gridLayout.addWidget(self.mMinLineEdit, 3, 2) + self.gridLayout.addWidget(self.mMaxLabel, 3, 3) + self.gridLayout.addWidget(self.mMaxLineEdit, 3, 4) + + self.gridLayout.addWidget(self.mContrastEnhancementLabel, 4, 0) + self.gridLayout.addWidget(self.mContrastEnhancementComboBox, 4, 1, 1 ,4) + self.gridLayout.setSpacing(2) + + self.setLayoutItemVisibility(self.gridLayout, True) + + self.mDefaultRenderer = layer.renderer() + self.setFromRenderer(self.mDefaultRenderer) + + def initActionButtons(self): + + wl, wlu = parseWavelength(self.rasterLayer()) + self.wavelengths = wl + self.wavelengthUnit = wlu + + self.mBtnBar.setLayout(QHBoxLayout()) + self.mBtnBar.layout().addStretch() + self.mBtnBar.layout().setContentsMargins(0, 0, 0, 0) + self.mBtnBar.layout().setSpacing(2) + + self.actionSetDefault = QAction('Default') + self.actionSetRed = QAction('R') + self.actionSetGreen = QAction('G') + self.actionSetBlue = QAction('B') + self.actionSetNIR = QAction('nIR') + self.actionSetSWIR = QAction('swIR') + + self.actionSetDefault.triggered.connect(lambda: self.setBandSelection('default')) + self.actionSetRed.triggered.connect(lambda: self.setBandSelection('R')) + self.actionSetGreen.triggered.connect(lambda: self.setBandSelection('G')) + self.actionSetBlue.triggered.connect(lambda: self.setBandSelection('B')) + self.actionSetNIR.triggered.connect(lambda: self.setBandSelection('nIR')) + self.actionSetSWIR.triggered.connect(lambda: self.setBandSelection('swIR')) + + + def addBtnAction(action): + btn = QToolButton() + btn.setDefaultAction(action) + self.mBtnBar.layout().addWidget(btn) + self.insertAction(None, action) + return btn + + self.btnDefault = addBtnAction(self.actionSetDefault) + self.btnBlue = addBtnAction(self.actionSetBlue) + self.btnGreen = addBtnAction(self.actionSetGreen) + self.btnRed = addBtnAction(self.actionSetRed) + self.btnNIR = addBtnAction(self.actionSetNIR) + self.btnSWIR = addBtnAction(self.actionSetSWIR) + + b = self.wavelengths is not None + for a in [self.actionSetRed, self.actionSetGreen, self.actionSetBlue, self.actionSetNIR, self.actionSetSWIR]: + a.setEnabled(b) + + + +class SingleBandPseudoColorRendererWidget(QgsSingleBandPseudoColorRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return SingleBandPseudoColorRendererWidget(layer, extent) + + def __init__(self, layer, extent): + super(SingleBandPseudoColorRendererWidget, self).__init__(layer, extent) + + self.mColormapTreeWidget.setMinimumSize(QSize(1,1)) + + self.gridLayout = self.layout() + assert isinstance(self.gridLayout, QGridLayout) + for i in range(self.gridLayout.count()): + w = self.gridLayout.itemAt(i) + w = w.widget() + if isinstance(w, QWidget): + setattr(self, w.objectName(), w) + + toReplace = [self.mBandComboBox,self.mMinLabel,self.mMaxLabel, self.mMinLineEdit, self.mMaxLineEdit ] + for w in toReplace: + self.gridLayout.removeWidget(w) + w.setVisible(False) + self.mBandSlider = QSlider(Qt.Horizontal) + self.mBandComboBoxes.append(self.mBandComboBox) + self.fixBandNames(self.mBandComboBox) + self.connectSliderWithBandComboBox(self.mBandSlider, self.mBandComboBox) + + self.mBtnBar = QFrame() + self.initActionButtons() + grid = QGridLayout() + grid.addWidget(self.mBtnBar,0,0,1,4, Qt.AlignLeft) + grid.addWidget(self.mBandSlider, 1,0, 1,2) + grid.addWidget(self.mBandComboBox, 1,2, 1,2) + grid.addWidget(self.mMinLabel, 2, 0) + grid.addWidget(self.mMinLineEdit, 2, 1) + grid.addWidget(self.mMaxLabel, 2, 2) + grid.addWidget(self.mMaxLineEdit, 2, 3) + #grid.setContentsMargins(2, 2, 2, 2, ) + grid.setColumnStretch(0, 0) + grid.setColumnStretch(1, 2) + grid.setColumnStretch(2, 0) + grid.setColumnStretch(3, 2) + grid.setSpacing(2) + self.gridLayout.addItem(grid, 0,1,2,4) + self.gridLayout.setSpacing(2) + self.setLayoutItemVisibility(grid, True) + + s = "" + + def initActionButtons(self): + wl, wlu = parseWavelength(self.rasterLayer()) + self.wavelengths = wl + self.wavelengthUnit = wlu + + self.mBtnBar.setLayout(QHBoxLayout()) + self.mBtnBar.layout().addStretch() + self.mBtnBar.layout().setContentsMargins(0, 0, 0, 0) + self.mBtnBar.layout().setSpacing(2) + + self.actionSetDefault = QAction('Default') + self.actionSetRed = QAction('R') + self.actionSetGreen = QAction('G') + self.actionSetBlue = QAction('B') + self.actionSetNIR = QAction('nIR') + self.actionSetSWIR = QAction('swIR') + + self.actionSetDefault.triggered.connect(lambda: self.setBandSelection('default')) + self.actionSetRed.triggered.connect(lambda: self.setBandSelection('R')) + self.actionSetGreen.triggered.connect(lambda: self.setBandSelection('G')) + self.actionSetBlue.triggered.connect(lambda: self.setBandSelection('B')) + self.actionSetNIR.triggered.connect(lambda: self.setBandSelection('nIR')) + self.actionSetSWIR.triggered.connect(lambda: self.setBandSelection('swIR')) + + + def addBtnAction(action): + btn = QToolButton() + btn.setDefaultAction(action) + self.mBtnBar.layout().addWidget(btn) + self.insertAction(None, action) + return btn + + self.btnDefault = addBtnAction(self.actionSetDefault) + self.btnBlue = addBtnAction(self.actionSetBlue) + self.btnGreen = addBtnAction(self.actionSetGreen) + self.btnRed = addBtnAction(self.actionSetRed) + self.btnNIR = addBtnAction(self.actionSetNIR) + self.btnSWIR = addBtnAction(self.actionSetSWIR) + + b = self.wavelengths is not None + for a in [self.actionSetRed, self.actionSetGreen, self.actionSetBlue, self.actionSetNIR, self.actionSetSWIR]: + a.setEnabled(b) + + +class PalettedRendererWidget(QgsPalettedRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return PalettedRendererWidget(layer, extent) + + def __init__(self, layer, extent): + super(PalettedRendererWidget, self).__init__(layer, extent) + + #self.modifyGridLayout() + + self.fixBandNames(self.mBandComboBox) + self.mTreeView.setMinimumSize(QSize(10,10)) + s = "" + + + +class MultiBandColorRendererWidget(QgsMultiBandColorRendererWidget, RendererWidgetModifications): + @staticmethod + def create(layer, extent): + return MultiBandColorRendererWidget(layer, extent) + + + def __init__(self, layer, extent): + super(MultiBandColorRendererWidget, self).__init__(layer, extent) + + self.modifyGridLayout() + + self.mRedBandSlider = QSlider(Qt.Horizontal) + self.mGreenBandSlider = QSlider(Qt.Horizontal) + self.mBlueBandSlider = QSlider(Qt.Horizontal) + + self.mBandComboBoxes.extend([self.mRedBandComboBox, self.mGreenBandComboBox, self.mBlueBandComboBox]) + self.mSliders = [self.mRedBandSlider, self.mGreenBandSlider, self.mBlueBandSlider] + nb = self.rasterLayer().dataProvider().bandCount() + for cbox, slider in zip(self.mBandComboBoxes, self.mSliders): + self.connectSliderWithBandComboBox(slider, cbox) + + + self.fixBandNames(self.mRedBandComboBox) + self.fixBandNames(self.mGreenBandComboBox) + self.fixBandNames(self.mBlueBandComboBox) + + self.mBtnBar = QFrame() + self.mBtnBar.setLayout(QHBoxLayout()) + self.initActionButtons() + self.mBtnBar.layout().addStretch() + self.mBtnBar.layout().setContentsMargins(0, 0, 0, 0) + self.mBtnBar.layout().setSpacing(2) + + #self.gridLayout.deleteLater() +# self.gridLayout = newGrid + self.gridLayout.addWidget(self.mBtnBar, 0, 1, 1, 3) + self.gridLayout.addWidget(self.mRedBandLabel, 1, 0) + self.gridLayout.addWidget(self.mRedBandSlider, 1, 1) + self.gridLayout.addWidget(self.mRedBandComboBox, 1, 2) + self.gridLayout.addWidget(self.mRedMinLineEdit, 1, 3) + self.gridLayout.addWidget(self.mRedMaxLineEdit, 1, 4) + + self.gridLayout.addWidget(self.mGreenBandLabel, 2, 0) + self.gridLayout.addWidget(self.mGreenBandSlider, 2, 1) + self.gridLayout.addWidget(self.mGreenBandComboBox, 2, 2) + self.gridLayout.addWidget(self.mGreenMinLineEdit, 2, 3) + self.gridLayout.addWidget(self.mGreenMaxLineEdit, 2, 4) + + self.gridLayout.addWidget(self.mBlueBandLabel, 3, 0) + self.gridLayout.addWidget(self.mBlueBandSlider, 3, 1) + self.gridLayout.addWidget(self.mBlueBandComboBox, 3, 2) + self.gridLayout.addWidget(self.mBlueMinLineEdit, 3, 3) + self.gridLayout.addWidget(self.mBlueMaxLineEdit, 3, 4) + + self.gridLayout.addWidget(self.mContrastEnhancementAlgorithmLabel, 4, 0, 1, 2) + self.gridLayout.addWidget(self.mContrastEnhancementAlgorithmComboBox, 4, 2, 1, 3) + + self.setLayoutItemVisibility(self.gridLayout, True) + + + self.mRedBandLabel.setText('R') + self.mGreenBandLabel.setText('G') + self.mBlueBandLabel.setText('B') + + self.mDefaultRenderer = layer.renderer() + + + + def initActionButtons(self): + + wl, wlu = parseWavelength(self.rasterLayer()) + self.wavelengths = wl + self.wavelengthUnit = wlu + + self.actionSetDefault = QAction('Default') + self.actionSetTrueColor = QAction('RGB') + self.actionSetCIR = QAction('nIR') + self.actionSet453 = QAction('swIR') + + self.actionSetDefault.triggered.connect(lambda: self.setBandSelection('default')) + self.actionSetTrueColor.triggered.connect(lambda: self.setBandSelection('R,G,B')) + self.actionSetCIR.triggered.connect(lambda: self.setBandSelection('nIR,R,G')) + self.actionSet453.triggered.connect(lambda: self.setBandSelection('nIR,swIR,R')) + + + def addBtnAction(action): + btn = QToolButton() + btn.setDefaultAction(action) + self.mBtnBar.layout().addWidget(btn) + self.insertAction(None, action) + return btn + + self.btnDefault = addBtnAction(self.actionSetDefault) + self.btnTrueColor = addBtnAction(self.actionSetTrueColor) + self.btnCIR = addBtnAction(self.actionSetCIR) + self.btn453 = addBtnAction(self.actionSet453) + + b = self.wavelengths is not None + for a in [self.actionSetCIR, self.actionSet453, self.actionSetTrueColor]: + a.setEnabled(b) + + class MapView(QObject): sigRemoveMapView = pyqtSignal(object) @@ -111,7 +997,7 @@ class MapView(QObject): #sigVectorVisibility = pyqtSignal(bool) #sigRasterVisibility = pyqtSignal(bool) - sigTitleChanged = pyqtSignal([str],[unicode]) + sigTitleChanged = pyqtSignal(str) sigSensorRendererChanged = pyqtSignal(SensorInstrument, QgsRasterRenderer) sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle) @@ -251,9 +1137,14 @@ class MapView(QObject): return self.ui.tbName.text() def refreshMapView(self, *args): - for mapCanvas in self.mapCanvases(): - assert isinstance(mapCanvas, MapCanvas) - mapCanvas.refresh() + + for renderSettings in self.mSensorViews.values(): + assert isinstance(renderSettings, MapViewRenderSettings) + renderSettings.applyStyle() + + #for mapCanvas in self.mapCanvases(): + # assert isinstance(mapCanvas, MapCanvas) + # mapCanvas.refresh() def setCrosshairStyle(self, crosshairStyle): if isinstance(crosshairStyle, CrosshairStyle): @@ -330,12 +1221,12 @@ class MapView(QObject): assert isinstance(mapCanvas, MapCanvas) assert isinstance(sensor, SensorInstrument) - sensorView = self.mSensorViews[sensor] - assert isinstance(sensorView, MapViewSensorSettings) - sensorView.registerMapCanvas(mapCanvas) + mapViewRenderSettings = self.mSensorViews[sensor] + assert isinstance(mapViewRenderSettings, MapViewRenderSettings) + mapViewRenderSettings.registerMapCanvas(mapCanvas) #register signals sensor specific signals - mapCanvas.setRenderer(sensorView.rasterLayerRenderer()) + mapCanvas.setRenderer(mapViewRenderSettings.rasterRenderer()) mapCanvas.setRenderer(self.vectorLayerRenderer()) @@ -374,13 +1265,63 @@ class MapView(QObject): assert type(sensor) is SensorInstrument return self.mSensorViews[sensor] +def displayBandNames(provider_or_dataset, bands=None): + results = None + if isinstance(provider_or_dataset, QgsRasterLayer): + return displayBandNames(provider_or_dataset.dataProvider()) + elif isinstance(provider_or_dataset, QgsRasterDataProvider): + if provider_or_dataset.name() == 'gdal': + ds = gdal.Open(provider_or_dataset.dataSourceUri()) + results = displayBandNames(ds, bands=bands) + else: + # same as in QgsRasterRendererWidget::displayBandName + results = [] + if bands is None: + bands = range(1, provider_or_dataset.bandCount() + 1) + for band in bands: + result = provider_or_dataset.generateBandName(band) + colorInterp ='{}'.format(provider_or_dataset.colorInterpretationName(band)) + if colorInterp != 'Undefined': + result += '({})'.format(colorInterp) + results.append(result) + + elif isinstance(provider_or_dataset, gdal.Dataset): + results = [] + if bands is None: + bands = range(1, provider_or_dataset.RasterCount+1) + for band in bands: + b = provider_or_dataset.GetRasterBand(band) + descr = b.GetDescription() + if len(descr) == 0: + descr = 'Band {}'.format(band) + results.append(descr) + return results -class MapViewRenderSettingsUI(QgsCollapsibleGroupBox, loadUI('mapviewrendersettings.ui')): - def __init__(self, parent=None): +class RasterDataProviderMockup(QgsRasterDataProvider): + + def __init__(self): + super(RasterDataProviderMockup, self).__init__('') + + + + +class MapViewRenderSettings(QgsCollapsibleGroupBox, loadUI('mapviewrendersettings.ui')): + + + LUT_RENDERER = {QgsMultiBandColorRenderer:QgsMultiBandColorRendererWidget, + QgsSingleBandGrayRenderer:QgsSingleBandGrayRendererWidget, + QgsSingleBandPseudoColorRenderer:QgsSingleBandPseudoColorRendererWidget, + QgsPalettedRasterRenderer:QgsPalettedRendererWidget} + LUT_RENDERER[QgsMultiBandColorRenderer]=MultiBandColorRendererWidget + LUT_RENDERER[QgsSingleBandPseudoColorRenderer]=SingleBandPseudoColorRendererWidget + LUT_RENDERER[QgsSingleBandGrayRenderer]=SingleBandGrayRendererWidget + LUT_RENDERER[QgsPalettedRasterRenderer] = PalettedRendererWidget + + def __init__(self, sensor, parent=None): """Constructor.""" - super(MapViewRenderSettingsUI, self).__init__(parent) + super(MapViewRenderSettings, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see @@ -389,435 +1330,246 @@ class MapViewRenderSettingsUI(QgsCollapsibleGroupBox, loadUI('mapviewrendersetti self.setupUi(self) - self.btnDefaultMB.setDefaultAction(self.actionSetDefaultMB) - self.btnTrueColor.setDefaultAction(self.actionSetTrueColor) - self.btnCIR.setDefaultAction(self.actionSetCIR) - self.btn453.setDefaultAction(self.actionSet453) + QApplication.clipboard().dataChanged.connect(self.onClipboardChanged) + + assert isinstance(sensor, SensorInstrument) + self.mSensor = sensor + self.mSensor.sigNameChanged.connect(self.onSensorNameChanged) + self.setTitle(self.mSensor.name()) + + from timeseriesviewer.models import OptionListModel, Option + rasterRendererModel = OptionListModel() + #rasterRendererModel.addOption(Option(QgsMultiBandColorRendererWidget, name='multibandcolor (QGIS)', mRenderType = QgsMultiBandColorRenderer)) + #rasterRendererModel.addOption(Option(QgsPalettedRendererWidget, name='paletted (QGIS)', mRenderType = QgsPalettedRasterRenderer)) + #rasterRendererModel.addOption(Option(QgsSingleBandGrayRendererWidget, name='singlegray (QGIS)', mRenderType = QgsSingleBandGrayRenderer)) + #rasterRendererModel.addOption(Option(QgsSingleBandPseudoColorRendererWidget, name='singlebandpseudocolor (QGIS)', mRenderType = QgsSingleBandPseudoColorRenderer)) + + rasterRendererModel.addOption(Option(MultiBandColorRendererWidget, name='Multibandcolor', mRenderType=QgsMultiBandColorRenderer)) + rasterRendererModel.addOption(Option(SingleBandGrayRendererWidget, name='Singlegray', mRenderType=QgsSingleBandGrayRenderer)) + rasterRendererModel.addOption(Option(SingleBandPseudoColorRendererWidget, name='Singleband Pseudocolor', mRenderType=QgsSingleBandPseudoColorRenderer)) + rasterRendererModel.addOption(Option(PalettedRendererWidget, name='Paletted', mRenderType=QgsPalettedRasterRenderer)) + self.mRasterRendererModel = rasterRendererModel + + self.cbRenderType.setModel(self.mRasterRendererModel) + assert isinstance(self.stackedWidget, QStackedWidget) + + self.mMockupCanvas = QgsMapCanvas() + self.mMockupRasterLayer = QgsRasterLayer(self.mSensor.pathImg) + self.mMockupCanvas.setLayers([self.mMockupRasterLayer]) + for func in rasterRendererModel.optionValues(): + + #extent = self.canvas.extent() + #w = func.create(self.mMockupRasterLayer, self.mMockupRasterLayer.extent()) + w = func(self.mMockupRasterLayer, self.mMockupRasterLayer.extent()) + #w = func(QgsRasterLayer(), QgsRectangle()) + + w.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)) + #w.sizeHint = lambda : QSize(300, 50) + w.setMapCanvas(self.mMockupCanvas) + + self.stackedWidget.addWidget(w) + + self.mMapCanvases = [] + self.initActions() + + def initActions(self): - self.btnSingleBandDef.setDefaultAction(self.actionSetDefaultSB) - self.btnSingleBandBlue.setDefaultAction(self.actionSetB) - self.btnSingleBandGreen.setDefaultAction(self.actionSetG) - self.btnSingleBandRed.setDefaultAction(self.actionSetR) - self.btnSingleBandNIR.setDefaultAction(self.actionSetNIR) - self.btnSingleBandSWIR.setDefaultAction(self.actionSetSWIR) self.btnPasteStyle.setDefaultAction(self.actionPasteStyle) self.btnCopyStyle.setDefaultAction(self.actionCopyStyle) self.btnApplyStyle.setDefaultAction(self.actionApplyStyle) + clipboardRenderer = rendererFromXml(QApplication.clipboard().mimeData()) + self.actionPasteStyle.setEnabled(isinstance(clipboardRenderer, QgsRasterRenderer)) + self.actionPasteStyle.triggered.connect(self.pasteStyleFromClipboard) + self.actionCopyStyle.triggered.connect(self.pasteStyleToClipboard) + self.actionApplyStyle.triggered.connect(self.applyStyle) + def mapCanvases(self): + return self.mMapCanvases[:] -class MapViewSensorSettings(QObject): - """ - Describes the rendering of images of one Sensor - """ + def registerMapCanvas(self, mapCanvas): - sigSensorRendererChanged = pyqtSignal(QgsRasterRenderer) + assert isinstance(mapCanvas, MapCanvas) + self.mMapCanvases.append(mapCanvas) + mapCanvas.sigChangeSVRequest.connect(self.onMapCanvasRendererChangeRequest) - def __init__(self, sensor, parent=None): - """Constructor.""" - super(MapViewSensorSettings, self).__init__(parent) - from timeseriesviewer.timeseries import SensorInstrument - assert isinstance(sensor, SensorInstrument) - self.sensor = sensor + def onMapCanvasRendererChangeRequest(self, mapCanvas, renderer): + self.setRasterRenderer(renderer) + self.applyStyle() - self.ui = MapViewRenderSettingsUI(parent) - self.ui.create() - self.ui.stackedWidget.currentChanged.connect(self.updateUi) - self.sensor.sigNameChanged.connect(self.onSensorNameChanged) - self.onSensorNameChanged(self.sensor.name()) - self.mMapCanvases = [] - self.ui.bandNames = sensor.bandNames - - self.multiBandMinValues = [self.ui.tbRedMin, self.ui.tbGreenMin, self.ui.tbBlueMin] - self.multiBandMaxValues = [self.ui.tbRedMax, self.ui.tbGreenMax, self.ui.tbBlueMax] - self.multiBandSliders = [self.ui.sliderRed, self.ui.sliderGreen, self.ui.sliderBlue] - - - - for tb in self.multiBandMinValues + self.multiBandMaxValues + [self.ui.tbSingleBandMin, self.ui.tbSingleBandMax]: - assert isinstance(tb, QLineEdit) - - tb.setValidator(QDoubleValidator()) - tb.textChanged.connect(self.onValuesChanged) - - for sl in self.multiBandSliders + [self.ui.sliderSingleBand]: - sl.setMinimum(1) - sl.setMaximum(sensor.nb) - sl.valueChanged.connect(self.updateUi) - - - - - self.ceAlgs = collections.OrderedDict() - - self.ceAlgs["No enhancement"] = QgsContrastEnhancement.NoEnhancement - self.ceAlgs["Stretch to MinMax"] = QgsContrastEnhancement.StretchToMinimumMaximum - self.ceAlgs["Stretch and clip to MinMax"] = QgsContrastEnhancement.StretchAndClipToMinimumMaximum - self.ceAlgs["Clip to MinMax"] = QgsContrastEnhancement.ClipToMinimumMaximum - - self.colorRampType = collections.OrderedDict() - self.colorRampType['Interpolated'] = QgsColorRampShader.Interpolated - self.colorRampType['Discrete'] = QgsColorRampShader.Discrete - self.colorRampType['Exact'] = QgsColorRampShader.Exact - - self.colorRampClassificationMode = collections.OrderedDict() - self.colorRampClassificationMode['Continuous'] = 1 - self.colorRampClassificationMode['Equal Interval'] = 2 - self.colorRampClassificationMode['Quantile'] = 3 - - def populateCombobox(cb, d): - for key, value in d.items(): - cb.addItem(key, value) - cb.setCurrentIndex(0) + def onSensorNameChanged(self, newName): + self.setTitle(self.mSensor.name()) + self.actionApplyStyle.setToolTip('Apply style to all map view images from "{}"'.format(self.mSensor.name())) - populateCombobox(self.ui.comboBoxContrastEnhancement, self.ceAlgs) - #populateCombobox(self.ui.cbSingleBandColorRampType, self.colorRampType) - populateCombobox(self.ui.cbSingleBandMode, self.colorRampClassificationMode) - #self.ui.cbSingleBandColorRamp.populate(QgsStyleV2.defaultStyle()) - self.ui.btnSingleBandColorRamp.setColorRamp(QgsStyle.defaultStyle().colorRamp('Greens')) + def currentRenderWidget(self): + """ + Returns the current QgsRasterRendererWidget + :return: QgsRasterRendererWidget + """ + return self.stackedWidget.currentWidget() - nb = self.sensor.nb - lyr = QgsRasterLayer(self.sensor.pathImg) + def setRasterRenderer(self, renderer): + assert isinstance(renderer, QgsRasterRenderer) + assert isinstance(self.stackedWidget, QStackedWidget) - #define default renderers: - bands = [min([b,nb-1]) for b in range(3)] - extent = lyr.extent() + self.mMockupRasterLayer.setRenderer(renderer) - bandStats = [lyr.dataProvider().bandStatistics(b, QgsRasterBandStats.All, extent, 500) for b in range(nb)] + #find the widget class that fits + cls = None - def createEnhancement(bandIndex): - bandIndex = min([nb - 1, bandIndex]) - e = QgsContrastEnhancement(self.sensor.bandDataType) - e.setMinimumValue(bandStats[bandIndex].Min) - e.setMaximumValue(bandStats[bandIndex].Max) - e.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum) - return e + for option in self.mRasterRendererModel: + if type(renderer) == option.mRenderType: + cls = option.value() + break + if cls == None: + return - self.defaultMB = QgsMultiBandColorRenderer(lyr.dataProvider(), bands[0], bands[1], bands[2]) - self.defaultMB.setRedContrastEnhancement(createEnhancement(bands[0])) - self.defaultMB.setGreenContrastEnhancement(createEnhancement(bands[1])) - self.defaultMB.setBlueContrastEnhancement(createEnhancement(bands[2])) + widgets = [] + for i in range(self.stackedWidget.count()): + w = self.stackedWidget.widget(i) + if isinstance(w, cls): + widgets.append(w) - self.defaultSB = QgsSingleBandPseudoColorRenderer(lyr.dataProvider(), 0, None) + if len(widgets) > 0: + for w in widgets: + assert isinstance(w, QgsRasterRendererWidget) - #colorRamp = self.ui.cbSingleBandColorRamp.currentColorRamp() - colorRamp = self.ui.btnSingleBandColorRamp.colorRamp() - #fix: QGIS 3.0 constructor - shaderFunc = QgsColorRampShader(bandStats[0].Min, bandStats[0].Max) - shaderFunc.setColorRampType(QgsColorRampShader.Interpolated) - shaderFunc.setClip(True) - nSteps = 5 - colorRampItems = [] - diff = bandStats[0].Max - bandStats[0].Min - for i in range(nSteps+1): - f = float(i) / nSteps - color = colorRamp.color(f) - value = bandStats[0].Min + diff * f - colorRampItems.append(QgsColorRampShader.ColorRampItem(value, color)) - shaderFunc.setColorRampItemList(colorRampItems) - shader = QgsRasterShader() - shader.setMaximumValue(bandStats[0].Max) - shader.setMinimumValue(bandStats[0].Min) - shader.setRasterShaderFunction(shaderFunc) - self.defaultSB.setShader(shader) - self.defaultSB.setClassificationMin(shader.minimumValue()) - self.defaultSB.setClassificationMax(shader.maximumValue()) - #init connect signals - self.ui.actionSetDefaultMB.triggered.connect(lambda : self.setBandSelection('defaultMB')) - self.ui.actionSetTrueColor.triggered.connect(lambda: self.setBandSelection('TrueColor')) - self.ui.actionSetCIR.triggered.connect(lambda: self.setBandSelection('CIR')) - self.ui.actionSet453.triggered.connect(lambda: self.setBandSelection('453')) - - self.ui.actionSetDefaultSB.triggered.connect(lambda: self.setBandSelection('defaultSB')) - self.ui.actionSetB.triggered.connect(lambda: self.setBandSelection('B')) - self.ui.actionSetG.triggered.connect(lambda: self.setBandSelection('G')) - self.ui.actionSetR.triggered.connect(lambda: self.setBandSelection('R')) - self.ui.actionSetNIR.triggered.connect(lambda: self.setBandSelection('nIR')) - self.ui.actionSetSWIR.triggered.connect(lambda: self.setBandSelection('swIR')) - - self.ui.actionApplyStyle.triggered.connect(self.applyStyle) - self.ui.actionCopyStyle.triggered.connect(lambda : QApplication.clipboard().setMimeData(self.mimeDataStyle())) - self.ui.actionPasteStyle.triggered.connect(lambda : self.pasteStyleFromClipboard()) - - #self.ui.stackedWidget - - if not self.sensor.wavelengthsDefined(): - self.ui.btnTrueColor.setEnabled(False) - self.ui.btnCIR.setEnabled(False) - self.ui.btn453.setEnabled(False) - - self.ui.btnSingleBandBlue.setEnabled(False) - self.ui.btnSingleBandGreen.setEnabled(False) - self.ui.btnSingleBandRed.setEnabled(False) - self.ui.btnSingleBandNIR.setEnabled(False) - self.ui.btnSingleBandSWIR.setEnabled(False) - - #apply recent or default renderer - renderer = lyr.renderer() - - #set defaults - self.setLayerRenderer(self.defaultSB) - self.setLayerRenderer(self.defaultMB) - - self.mLastRenderer = renderer - if type(renderer) in [QgsMultiBandColorRenderer, QgsSingleBandPseudoColorRenderer]: - self.setLayerRenderer(renderer) - - - QApplication.clipboard().dataChanged.connect(self.onClipboardChange) - self.onClipboardChange() + #w.setRasterLayer(self.mMockupRasterLayer) + #w.setFromRenderer(cloneRenderer(renderer)) + w.setFromRenderer(renderer) + #w.doComputations() - def mapCanvases(self): - return self.mMapCanvases[:] + w = widgets[0] + self.stackedWidget.setCurrentWidget(w) - def registerMapCanvas(self, mapCanvas): + self.cbRenderType.setCurrentIndex(self.stackedWidget.currentIndex()) - assert isinstance(mapCanvas, MapCanvas) - self.mMapCanvases.append(mapCanvas) - mapCanvas.sigChangeSVRequest.connect(self.onMapCanvasRendererChangeRequest) + def rasterRenderer(self): + + return self.stackedWidget.currentWidget().renderer() + + + def apply(self): + + mRendererWidget = self.currentRenderWidget() + mRendererWidget.doComputations() + + def onClipboardChanged(self): + mimeData = QApplication.clipboard().mimeData() + renderer = rendererFromXml(mimeData) + b = isinstance(renderer, QgsRasterRenderer) + #if b == False: + # print(mimeData.formats()) + # s = "" + self.actionPasteStyle.setEnabled(b) - def onSensorNameChanged(self, newName): - self.ui.setTitle(self.sensor.name()) - self.ui.actionApplyStyle.setToolTip('Apply style to all map view images from "{}"'.format(self.sensor.name())) def pasteStyleFromClipboard(self): - utils = TsvMimeDataUtils(QApplication.clipboard().mimeData()) - if utils.hasRasterStyle(): - renderer = utils.rasterStyle(self.sensor.bandDataType) - if renderer is not None: - self.setLayerRenderer(renderer) + mimeData = QApplication.clipboard().mimeData() + renderer = rendererFromXml(mimeData) + if isinstance(renderer, QgsRasterRenderer): + self.setRasterRenderer(renderer) + + def pasteStyleToClipboard(self): + xml = rendererToXml(self.rasterRenderer()) + assert isinstance(xml, QDomDocument) + md = QMimeData() + #['application/qgis.style', 'text/plain'] + + md.setData('application/qgis.style', xml.toByteArray()) + md.setData('text/plain', xml.toByteArray()) + QApplication.clipboard().setMimeData(md) def applyStyle(self, *args): - r = self.rasterLayerRenderer() + + r = self.rasterRenderer() for mapCanvas in self.mMapCanvases: assert isinstance(mapCanvas, MapCanvas) mapCanvas.layerModel().setRenderer(r) mapCanvas.refresh() - def onClipboardChange(self): - utils = TsvMimeDataUtils(QApplication.clipboard().mimeData()) - self.ui.btnPasteStyle.setEnabled(utils.hasRasterStyle()) - def onMapCanvasRendererChangeRequest(self, mapCanvas, renderer): - self.setLayerRenderer(renderer) - def setBandSelection(self, key): +RENDER_CLASSES = {} +RENDER_CLASSES['rasterrenderer'] = { + 'singlebandpseudocolor':QgsSingleBandPseudoColorRenderer, + 'singlebandgray': QgsSingleBandGrayRenderer, + 'paletted':QgsPalettedRasterRenderer, + 'multibandcolor': QgsMultiBandColorRenderer, + 'hillshade': QgsHillshadeRenderer +} +RENDER_CLASSES['renderer-v2'] = { + 'categorizedSymbol':QgsCategorizedSymbolRenderer, + 'singleSymbol':QgsSingleSymbolRenderer +} - if key == 'defaultMB': - bands = [self.defaultMB.redBand(), self.defaultMB.greenBand(), self.defaultMB.blueBand()] - elif key == 'defaultSB': - bands = [self.defaultSB.band()] - else: - if key in ['R','G','B','nIR','swIR']: - colors = [key] - elif key == 'TrueColor': - colors = ['R','G','B'] - elif key == 'CIR': - colors = ['nIR', 'R', 'G'] - elif key == '453': - colors = ['nIR','swIR', 'R'] - bands = [self.sensor.bandClosestToWavelength(c) for c in colors] - - if len(bands) == 1: - self.ui.sliderSingleBand.setValue(bands[0]+1) - elif len(bands) == 3: - for i, b in enumerate(bands): - self.multiBandSliders[i].setValue(b+1) - - - - - SignalizeImmediately = True - - def onValuesChanged(self, text): - styleValid = "" - styleInValid = """.QLineEdit {border-color:red; - border-style: outset; - border-width: 2px; - background-color: yellow } - """ - w = self.sender() - if isinstance(w, QLineEdit): - validator = w.validator() - assert isinstance(validator, QDoubleValidator) - res = validator.validate(text, 0) - if res[0] == QDoubleValidator.Acceptable: - w.setStyleSheet(styleValid) - else: - w.setStyleSheet(styleInValid) +def rendererToXml(renderer): + doc = QDomDocument() + if isinstance(renderer, QgsRasterRenderer): + path = os.path.join(os.path.dirname(__file__), 'dummy_gdal.vrt') + lyr = QgsRasterLayer(path) + lyr.setRenderer(renderer.clone()) - def updateUi(self, *args): + elif isinstance(renderer, QgsFeatureRenderer): + #todo: distinguish vector type from requested renderer + lyr = QgsVectorLayer('Point?crs=epsg:4326&field=id:integer', 'dummy', 'memory') + lyr.setRenderer(renderer.clone()) + else: + raise NotImplementedError() + err = '' + lyr.exportNamedStyle(doc, err) + return doc - cw = self.ui.stackedWidget.currentWidget() - text = '' - if cw == self.ui.pageMultiBand: - text = 'Multiband({} {} {})'.format( - self.ui.sliderRed.value(), - self.ui.sliderGreen.value(), - self.ui.sliderBlue.value() - ) - elif cw == self.ui.pageSingleBand: - text = 'Singleband({})'.format(self.ui.sliderSingleBand.value()) - text = '{} - {}'.format(self.sensor.name(), text) - self.ui.setTitle(text) +def rendererFromXml(xml): + """ + Reads a string `text` and returns the first QgsRasterRenderer or QgsFeatureRenderer (if defined). + :param text: + :return: + """ + + if isinstance(xml, QMimeData): + for format in ['application/qgis.style', 'text/plain']: + if format in xml.formats(): + dom = QDomDocument() + dom.setContent(xml.data(format)) + return rendererFromXml(dom) + return None + + elif isinstance(xml, str): + dom = QDomDocument() + dom.setContent(xml) + return rendererFromXml(dom) + + assert isinstance(xml, QDomDocument) + root = xml.documentElement() + for baseClass, renderClasses in RENDER_CLASSES.items(): + elements = root.elementsByTagName(baseClass) + if elements.count() > 0: + elem = elements.item(0).toElement() + typeName = elem.attributes().namedItem('type').nodeValue() + if typeName in renderClasses.keys(): + rClass = renderClasses[typeName] + if baseClass == 'rasterrenderer': + + return rClass.create(elem, DUMMY_RASTERINTERFACE) + elif baseClass == 'renderer-v2': + context = QgsReadWriteContext() + return rClass.load(elem, context) + #return rClass.create(elem) + else: + print(typeName) + s ="" + return None - def setLayerRenderer(self, renderer): - ui = self.ui - assert isinstance(renderer, QgsRasterRenderer) - from timeseriesviewer.utils import niceNumberString - updated = False - if isinstance(renderer, QgsMultiBandColorRenderer): - self.ui.cbRenderType.setCurrentIndex(0) - #self.ui.stackedWidget.setcurrentWidget(self.ui.pageMultiBand) - - for s in self.multiBandSliders: - s.blockSignals(True) - ui.sliderRed.setValue(renderer.redBand()) - ui.sliderGreen.setValue(renderer.greenBand()) - ui.sliderBlue.setValue(renderer.blueBand()) - for s in self.multiBandSliders: - s.blockSignals(False) - - - ceRed = renderer.redContrastEnhancement() - ceGreen = renderer.greenContrastEnhancement() - ceBlue = renderer.blueContrastEnhancement() - - if ceRed is None: - ceRed = ceGreen = ceBlue = QgsContrastEnhancement(self.sensor.bandDataType) - s = "" - for i, ce in enumerate([ceRed, ceGreen, ceBlue]): - vMin = ce.minimumValue() - vMax = ce.maximumValue() - self.multiBandMinValues[i].setText(niceNumberString(vMin)) - self.multiBandMaxValues[i].setText(niceNumberString(vMax)) - - idx = list(self.ceAlgs.values()).index(ceRed.contrastEnhancementAlgorithm()) - ui.comboBoxContrastEnhancement.setCurrentIndex(idx) - - - updated = True - - if isinstance(renderer, QgsSingleBandPseudoColorRenderer): - self.ui.cbRenderType.setCurrentIndex(1) - #self.ui.stackedWidget.setCurrentWidget(self.ui.pageSingleBand) - - self.ui.sliderSingleBand.setValue(renderer.band()) - shader = renderer.shader() - cmin = shader.minimumValue() - cmax = shader.maximumValue() - self.ui.tbSingleBandMin.setText(str(cmin)) - self.ui.tbSingleBandMax.setText(str(cmax)) - - shaderFunc = shader.rasterShaderFunction() - #self.ui.cbSingleBandColorRampType.setCurrentIndex(shaderFunc.colorRampType()) - colorRamp = shaderFunc.sourceColorRamp() - if isinstance(colorRamp, QgsColorRamp): - self.ui.btnSingleBandColorRamp.setColorRamp(colorRamp) - updated = True - - self.updateUi() - if updated: - self.mLastRenderer = self.rasterLayerRenderer() - - if updated and MapViewSensorSettings.SignalizeImmediately: - self.sigSensorRendererChanged.emit(renderer.clone()) - self.applyStyle() - - def mimeDataStyle(self): - mimeData = QMimeData() - r = self.rasterLayerRenderer() - if isinstance(r, QgsRasterRenderer): - doc = QDomDocument() - lyr = QgsRasterLayer(self.sensor.pathImg) - lyr.setRenderer(self.rasterLayerRenderer()) - err = '' - lyr.exportNamedStyle(doc, err) - if len(err) == 0: - mimeData.setData('application/qgis.style', doc.toByteArray()) - mimeData.setText(doc.toString()) - return mimeData - - - - return mimeData - - def currentComboBoxItem(self, cb): - d = cb.itemData(cb.currentIndex(), Qt.UserRole) - return d - - def rasterLayerRenderer(self): - ui = self.ui - r = None - if ui.stackedWidget.currentWidget() == ui.pageMultiBand: - r = self.rasterRendererMultiBand(ui) - - if ui.stackedWidget.currentWidget() == ui.pageSingleBand: - r = self.rasterRendererSingleBand(ui) - return r - - def rasterRendererMultiBand(self, ui): - r = QgsMultiBandColorRenderer(None, - ui.sliderRed.value(), ui.sliderGreen.value(), ui.sliderBlue.value()) - i = self.ui.comboBoxContrastEnhancement.currentIndex() - alg = self.ui.comboBoxContrastEnhancement.itemData(i) - if alg == QgsContrastEnhancement.NoEnhancement: - r.setRedContrastEnhancement(None) - r.setGreenContrastEnhancement(None) - r.setBlueContrastEnhancement(None) - else: - rgbEnhancements = [] - for i in range(3): - e = QgsContrastEnhancement(self.sensor.bandDataType) - minmax = [float(self.multiBandMinValues[i].text()), float(self.multiBandMaxValues[i].text())] - cmin = min(minmax) - cmax = max(minmax) - e.setMinimumValue(cmin) - e.setMaximumValue(cmax) - e.setContrastEnhancementAlgorithm(alg) - rgbEnhancements.append(e) - r.setRedContrastEnhancement(rgbEnhancements[0]) - r.setGreenContrastEnhancement(rgbEnhancements[1]) - r.setBlueContrastEnhancement(rgbEnhancements[2]) - return r - - def rasterRendererSingleBand(self, ui): - r = QgsSingleBandPseudoColorRenderer(None, ui.sliderSingleBand.value(), None) - minmax = [float(ui.tbSingleBandMin.text()), float(ui.tbSingleBandMax.text())] - cmin = min(minmax) - cmax = max(minmax) - r.setClassificationMin(cmin) - r.setClassificationMax(cmax) - #colorRamp = self.ui.cbSingleBandColorRamp.currentColorRamp() - colorRamp = self.ui.btnSingleBandColorRamp.colorRamp() - # fix: QGIS 3.0 constructor - shaderFunc = QgsColorRampShader(cmin, cmax) - shaderFunc.setColorRampType(self.currentComboBoxItem(ui.cbSingleBandColorRampType)) - shaderFunc.setClip(True) - nSteps = 10 - colorRampItems = [] - diff = cmax - cmin - for i in range(nSteps + 1): - f = float(i) / nSteps - color = colorRamp.color(f) - value = cmin + diff * f - colorRampItems.append(QgsColorRampShader.ColorRampItem(value, color)) - shaderFunc.setColorRampItemList(colorRampItems) - shader = QgsRasterShader() - shader.setMaximumValue(cmax) - shader.setMinimumValue(cmin) - shader.setRasterShaderFunction(shaderFunc) - r.setShader(shader) - return r @@ -1914,29 +2666,39 @@ class MapViewCollectionDock(QgsDockWidget, loadUI('mapviewdock.ui')): if __name__ == '__main__': from timeseriesviewer import utils from timeseriesviewer.mapcanvas import MapCanvas - from example.Images import Img_2014_01_15_LC82270652014015LGN00_BOA + from example.Images import Img_2014_01_15_LC82270652014015LGN00_BOA, re_2014_06_25 from example import exampleEvents - qgsApp = utils.initQgisApplication() - - TS= TimeSeries() - dock = MapViewCollectionDock() - dock.setTimeSeries(TS) - dock.show() - mv1 = dock.createMapView() - mv2 = dock.createMapView() - dock.setCurrentMapView(mv1) - assert dock.currentMapView() == mv1 - dock.setCurrentMapView(mv2) - assert dock.currentMapView() == mv2 - - vl = QgsVectorLayer(exampleEvents, 'ogr') - QgsProject.instance().addMapLayer(vl) - #d = QgsRendererPropertiesDialog(vl, QgsStyle.defaultStyle()) - #d.show() - - TS.addFiles(Img_2014_01_15_LC82270652014015LGN00_BOA) + rDir = r'C:\Users\geo_beja\Repositories\QGIS_Plugins\eo-time-series-viewer\qgisresources' + qgsApp = utils.initQgisApplication(qgisResourceDir=rDir) + TS = TimeSeries() + TS.addFiles([Img_2014_01_15_LC82270652014015LGN00_BOA, re_2014_06_25]) + + if False: + + dock = MapViewCollectionDock() + dock.setTimeSeries(TS) + dock.show() + mv1 = dock.createMapView() + mv2 = dock.createMapView() + dock.setCurrentMapView(mv1) + assert dock.currentMapView() == mv1 + dock.setCurrentMapView(mv2) + assert dock.currentMapView() == mv2 + + vl = QgsVectorLayer(exampleEvents, 'ogr') + QgsProject.instance().addMapLayer(vl) + #d = QgsRendererPropertiesDialog(vl, QgsStyle.defaultStyle()) + #d.show() + + else: + + w = MapViewRenderSettings(TS.sensors()[0]) + w.show() + w.setRasterRenderer(w.rasterRenderer()) + #renderer2 = w.rasterRenderer() + #print(renderer2) qgsApp.exec_() \ No newline at end of file diff --git a/timeseriesviewer/models.py b/timeseriesviewer/models.py index 7ec06c87f28a090b7c6ca652f3f4623ebb3c92a6..06fa53c8c37e31fed89f395ff5de92749c8ad78d 100644 --- a/timeseriesviewer/models.py +++ b/timeseriesviewer/models.py @@ -71,14 +71,18 @@ def setCurrentComboBoxValue(comboBox, value): class Option(object): - def __init__(self, value, name=None, tooltip='', icon=QIcon()): + def __init__(self, value, name=None, tooltip='', icon=None, **kwargs ): self.mValue = value if name is None: name = str(value) self.mName = name self.mTooltip = tooltip - self.mIcon = None + self.mIcon = icon + + for k, v in kwargs.items(): + assert k not in self.__dict__.keys() + self.__dict__[k] = v def value(self): return self.mValue @@ -110,6 +114,10 @@ class OptionListModel(QAbstractListModel): def __iter__(self): return iter(self.mOptions) + def __len__(self): + return len(self.mOptions) + + def containsValue(self, value): return value in self.optionValues() diff --git a/timeseriesviewer/sandbox.py b/timeseriesviewer/sandbox.py index df736003fe8bf7ca6668633db7d6fea21a6cc5ee..1e0c054011f07182fe4dab5f208ccf5e32744235 100644 --- a/timeseriesviewer/sandbox.py +++ b/timeseriesviewer/sandbox.py @@ -20,7 +20,7 @@ """ # noinspection PyPep8Naming -import six, sys, os, gc, re, collections, site, inspect +import sys, os, gc, re, collections, site, inspect from osgeo import gdal, ogr from qgis import * from qgis.core import * @@ -352,10 +352,10 @@ if __name__ == '__main__': timeseriesviewer.DEBUG = True #run tests if False: gdal_qgis_benchmark() - if True: sandboxQgisBridge() + if False: sandboxQgisBridge() if False: sandboxGui() - if False: sandboxTestdata() + if True: sandboxTestdata() if False: sandboxDemo() #close QGIS qgsApp.exec_() diff --git a/timeseriesviewer/timeseries.py b/timeseriesviewer/timeseries.py index a96a57d009f5b295d29378abbbff7910eec48c78..0d4d8df76803628c8ae73618b4f155b6a32a3584 100644 --- a/timeseriesviewer/timeseries.py +++ b/timeseriesviewer/timeseries.py @@ -496,7 +496,7 @@ class TimeSeries(QObject): _sep = ';' def sensors(self): - return self.Sensors.keys() + return list(self.Sensors.keys()) def loadFromFile(self, path, n_max=None): @@ -745,7 +745,7 @@ class TimeSeriesTableModel(QAbstractTableModel): self.dataChanged.emit(idx, idx) def sensorsChanged(self, sensor): - i = self.mColumnNames.index('sensor') + i = self.mColumnNames.index(self.cnSensor) idx0 = self.createIndex(0, i) idx1 = self.createIndex(self.rowCount(), i) self.dataChanged.emit(idx0, idx1) diff --git a/timeseriesviewer/ui/mapviewdefinition.ui b/timeseriesviewer/ui/mapviewdefinition.ui index 0d86aecfbbdb4fc1cc724bbf72bd884fe3cbf6b9..737e1d80bea6bd0b8162f6719494e50436919a16 100644 --- a/timeseriesviewer/ui/mapviewdefinition.ui +++ b/timeseriesviewer/ui/mapviewdefinition.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>286</width> - <height>339</height> + <width>322</width> + <height>213</height> </rect> </property> <property name="sizePolicy"> @@ -18,7 +18,7 @@ </property> <property name="maximumSize"> <size> - <width>290</width> + <width>16777215</width> <height>16777215</height> </size> </property> @@ -75,20 +75,20 @@ <rect> <x>0</x> <y>0</y> - <width>263</width> - <height>333</height> + <width>299</width> + <height>500</height> </rect> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> + <horstretch>2</horstretch> <verstretch>2</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>0</width> - <height>0</height> + <height>500</height> </size> </property> <layout class="QVBoxLayout" name="verticalLayout_3"> @@ -96,7 +96,7 @@ <number>2</number> </property> <property name="sizeConstraint"> - <enum>QLayout::SetFixedSize</enum> + <enum>QLayout::SetNoConstraint</enum> </property> <property name="leftMargin"> <number>2</number> @@ -237,7 +237,7 @@ <item> <widget class="QgsCollapsibleGroupBox" name="gbVectorRendering"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -245,7 +245,7 @@ <property name="minimumSize"> <size> <width>0</width> - <height>25</height> + <height>35</height> </size> </property> <property name="title"> @@ -328,7 +328,7 @@ <property name="minimumSize"> <size> <width>0</width> - <height>25</height> + <height>35</height> </size> </property> <property name="title"> @@ -356,25 +356,6 @@ <property name="bottomMargin"> <number>0</number> </property> - <item> - <layout class="QVBoxLayout" name="renderSettingsLayout"> - <property name="sizeConstraint"> - <enum>QLayout::SetMinAndMaxSize</enum> - </property> - <property name="leftMargin"> - <number>9</number> - </property> - <property name="topMargin"> - <number>9</number> - </property> - <property name="rightMargin"> - <number>9</number> - </property> - <property name="bottomMargin"> - <number>9</number> - </property> - </layout> - </item> </layout> </widget> </item> @@ -383,10 +364,13 @@ <property name="orientation"> <enum>Qt::Vertical</enum> </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> <property name="sizeHint" stdset="0"> <size> <width>0</width> - <height>25</height> + <height>0</height> </size> </property> </spacer> diff --git a/timeseriesviewer/ui/mapviewrendersettings.ui b/timeseriesviewer/ui/mapviewrendersettings.ui index a5f17f42924ae5cb396d7df179087dea47aa3537..95f46df3d3d7434f48797fe1087ccab5dcc2892b 100644 --- a/timeseriesviewer/ui/mapviewrendersettings.ui +++ b/timeseriesviewer/ui/mapviewrendersettings.ui @@ -6,12 +6,12 @@ <rect> <x>0</x> <y>0</y> - <width>247</width> - <height>250</height> + <width>335</width> + <height>135</height> </rect> </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -24,8 +24,8 @@ </property> <property name="maximumSize"> <size> - <width>16000</width> - <height>16000</height> + <width>16777215</width> + <height>16777215</height> </size> </property> <property name="font"> @@ -48,12 +48,12 @@ <property name="checkable"> <bool>false</bool> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,2,1"> <property name="spacing"> <number>2</number> </property> <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> + <enum>QLayout::SetNoConstraint</enum> </property> <property name="leftMargin"> <number>2</number> @@ -90,7 +90,7 @@ <string>Sensor name</string> </property> <property name="text"> - <string>Render type</string> + <string>Style</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> @@ -101,7 +101,7 @@ <widget class="QComboBox" name="cbRenderType"> <property name="maximumSize"> <size> - <width>100</width> + <width>16777215</width> <height>16777215</height> </size> </property> @@ -110,16 +110,6 @@ <pointsize>8</pointsize> </font> </property> - <item> - <property name="text"> - <string>Multi</string> - </property> - </item> - <item> - <property name="text"> - <string>Single</string> - </property> - </item> </widget> </item> <item> @@ -190,917 +180,31 @@ <property name="minimumSize"> <size> <width>0</width> - <height>130</height> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> </size> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="currentIndex"> - <number>1</number> + <number>-1</number> </property> - <widget class="QWidget" name="pageMultiBand"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>90</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,2,2,2" columnstretch="0,0,0,0" rowminimumheight="0,0,0,0,0" columnminimumwidth="0,75,0,0"> - <property name="sizeConstraint"> - <enum>QLayout::SetMinAndMaxSize</enum> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>2</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>2</number> - </property> - <property name="spacing"> - <number>2</number> - </property> - <item row="2" column="1"> - <widget class="QSlider" name="sliderRed"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>1</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>24</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>6</number> - </property> - <property name="value"> - <number>3</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>1</number> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QLabel" name="labelMax"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Min</string> - </property> - </widget> - </item> - <item row="2" column="3"> - <widget class="QLineEdit" name="tbRedMax"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>5000</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QSlider" name="sliderGreen"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>24</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>6</number> - </property> - <property name="value"> - <number>2</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>1</number> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="labelGreen"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="text"> - <string>G</string> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="labelBlue"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>B</string> - </property> - </widget> - </item> - <item row="1" column="3"> - <widget class="QLabel" name="labelMin"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Max</string> - </property> - </widget> - </item> - <item row="3" column="3"> - <widget class="QLineEdit" name="tbGreenMax"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>5000</string> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QLineEdit" name="tbGreenMin"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>0</string> - </property> - </widget> - </item> - <item row="4" column="3"> - <widget class="QLineEdit" name="tbBlueMax"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>5000</string> - </property> - </widget> - </item> - <item row="4" column="2"> - <widget class="QLineEdit" name="tbBlueMin"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>0</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QSlider" name="sliderBlue"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>24</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>6</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>1</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="labelRed"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="text"> - <string>R</string> - </property> - </widget> - </item> - <item row="2" column="2"> - <widget class="QLineEdit" name="tbRedMin"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>0</string> - </property> - </widget> - </item> - <item row="0" column="0" colspan="4"> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <property name="spacing"> - <number>2</number> - </property> - <property name="sizeConstraint"> - <enum>QLayout::SetMinimumSize</enum> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <item> - <widget class="QToolButton" name="btnDefaultMB"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - <italic>false</italic> - </font> - </property> - <property name="toolTip"> - <string>default band selection</string> - </property> - <property name="text"> - <string>Def</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnTrueColor"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - <italic>false</italic> - </font> - </property> - <property name="toolTip"> - <string>red-green-blue (true colour)</string> - </property> - <property name="text"> - <string>TC</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnCIR"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - <italic>false</italic> - </font> - </property> - <property name="toolTip"> - <string>swIR-red-green (coloured infra-red)</string> - </property> - <property name="text"> - <string>cIR</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btn453"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - <italic>false</italic> - </font> - </property> - <property name="toolTip"> - <string>swIR-mwIR-red</string> - </property> - <property name="text"> - <string>swIR</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item row="1" column="0" colspan="2"> - <widget class="QLabel" name="labelMax_3"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Bands</string> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="pageSingleBand"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>90</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>2</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>2</number> - </property> - <property name="spacing"> - <number>2</number> - </property> - <item row="2" column="0" colspan="2"> - <widget class="QLabel" name="label_2"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Band</string> - </property> - </widget> - </item> - <item row="3" column="3"> - <widget class="QLineEdit" name="tbSingleBandMax"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>40</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>5000</string> - </property> - </widget> - </item> - <item row="2" column="3"> - <widget class="QLabel" name="labelMax_2"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Max</string> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QLineEdit" name="tbSingleBandMin"> - <property name="minimumSize"> - <size> - <width>40</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>0</string> - </property> - </widget> - </item> - <item row="2" column="2"> - <widget class="QLabel" name="labelMin_2"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Min</string> - </property> - </widget> - </item> - <item row="0" column="0" colspan="4"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>2</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <item> - <widget class="QToolButton" name="btnSingleBandDef"> - <property name="font"> - <font> - <pointsize>8</pointsize> - <italic>false</italic> - </font> - </property> - <property name="text"> - <string>Def</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnSingleBandBlue"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Select band from visible blue</string> - </property> - <property name="text"> - <string>B</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnSingleBandGreen"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Select band from visible green</string> - </property> - <property name="text"> - <string>G</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnSingleBandRed"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Select band from visible red</string> - </property> - <property name="text"> - <string>R</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnSingleBandNIR"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Select band from near infra-red</string> - </property> - <property name="text"> - <string>nIR</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnSingleBandSWIR"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Select band from shortwave infra-red</string> - </property> - <property name="text"> - <string>swIR</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item row="3" column="0" colspan="2"> - <widget class="QSlider" name="sliderSingleBand"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>6</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksAbove</enum> - </property> - <property name="tickInterval"> - <number>1</number> - </property> - </widget> - </item> - <item row="4" column="1" colspan="3"> - <widget class="QComboBox" name="cbSingleBandColorRampType"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>1</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Type of color ramp</string> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_4"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Interpolation</string> - </property> - </widget> - </item> - <item row="6" column="1" colspan="3"> - <widget class="QComboBox" name="cbSingleBandMode"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>1</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Color classification</string> - </property> - </widget> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_3"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Color</string> - </property> - </widget> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_5"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Mode</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QgsColorRampButton" name="btnSingleBandColorRamp"> - <property name="text"> - <string>...</string> - </property> - </widget> - </item> - </layout> - </widget> </widget> </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <property name="spacing"> - <number>2</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="label"> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>Contrast -enhancement</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="comboBoxContrastEnhancement"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>1</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <pointsize>8</pointsize> - </font> - </property> - </widget> - </item> - </layout> - </item> <item> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> <property name="sizeHint" stdset="0"> <size> <width>0</width> @@ -1209,13 +313,6 @@ enhancement</string> </property> </action> </widget> - <customwidgets> - <customwidget> - <class>QgsColorRampButton</class> - <extends>QToolButton</extends> - <header>qgis.gui</header> - </customwidget> - </customwidgets> <resources> <include location="resources.qrc"/> </resources> diff --git a/timeseriesviewer/ui/mapviewrendersettingsV2.ui b/timeseriesviewer/ui/mapviewrendersettingsV2.ui new file mode 100644 index 0000000000000000000000000000000000000000..95f46df3d3d7434f48797fe1087ccab5dcc2892b --- /dev/null +++ b/timeseriesviewer/ui/mapviewrendersettingsV2.ui @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MapViewRenderSettings</class> + <widget class="QGroupBox" name="MapViewRenderSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>335</width> + <height>135</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>150</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + <property name="font"> + <font> + <pointsize>8</pointsize> + </font> + </property> + <property name="windowTitle"> + <string>RenderSettings</string> + </property> + <property name="styleSheet"> + <string notr="true">QFrame::title{color: rgb(0, 0, 127);}</string> + </property> + <property name="title"> + <string><sensor/product></string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,2,1"> + <property name="spacing"> + <number>2</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetNoConstraint</enum> + </property> + <property name="leftMargin"> + <number>2</number> + </property> + <property name="topMargin"> + <number>6</number> + </property> + <property name="rightMargin"> + <number>2</number> + </property> + <property name="bottomMargin"> + <number>2</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="spacing"> + <number>2</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="labelRenderer"> + <property name="font"> + <font> + <pointsize>8</pointsize> + <weight>50</weight> + <italic>false</italic> + <bold>false</bold> + <kerning>true</kerning> + </font> + </property> + <property name="toolTip"> + <string>Sensor name</string> + </property> + <property name="text"> + <string>Style</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbRenderType"> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + <property name="font"> + <font> + <pointsize>8</pointsize> + </font> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnPasteStyle"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/mActionEditPaste.svg</normaloff>:/timeseriesviewer/icons/mActionEditPaste.svg</iconset> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnCopyStyle"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/mActionEditCopy.svg</normaloff>:/timeseriesviewer/icons/mActionEditCopy.svg</iconset> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnApplyStyle"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/mActionRefresh.png</normaloff>:/timeseriesviewer/icons/mActionRefresh.png</iconset> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="currentIndex"> + <number>-1</number> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + <action name="actionSetDefaultMB"> + <property name="text"> + <string>Def</string> + </property> + <property name="toolTip"> + <string>Set default band selection</string> + </property> + </action> + <action name="actionSetTrueColor"> + <property name="text"> + <string>True</string> + </property> + <property name="toolTip"> + <string>Set to true color (red-green-blue)</string> + </property> + </action> + <action name="actionSetCIR"> + <property name="text"> + <string>CIR1</string> + </property> + <property name="toolTip"> + <string>Set to coloured infra red (swIR-red-green)</string> + </property> + </action> + <action name="actionSet453"> + <property name="text"> + <string>CIR2</string> + </property> + <property name="toolTip"> + <string>Set to swIR-mwIR-red</string> + </property> + </action> + <action name="actionCopyStyle"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/mActionEditCopy.svg</normaloff>:/timeseriesviewer/icons/mActionEditCopy.svg</iconset> + </property> + <property name="text"> + <string>Copy style</string> + </property> + <property name="toolTip"> + <string>Copy style to clipboard</string> + </property> + </action> + <action name="actionPasteStyle"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/mActionEditPaste.svg</normaloff>:/timeseriesviewer/icons/mActionEditPaste.svg</iconset> + </property> + <property name="text"> + <string>Paste Style</string> + </property> + <property name="toolTip"> + <string>Paste style from clipboard</string> + </property> + </action> + <action name="actionApplyStyle"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/mActionRefresh.png</normaloff>:/timeseriesviewer/icons/mActionRefresh.png</iconset> + </property> + <property name="text"> + <string>ApplySettings</string> + </property> + <property name="toolTip"> + <string>Apply Style</string> + </property> + </action> + <action name="actionSetR"> + <property name="text"> + <string>R</string> + </property> + </action> + <action name="actionSetG"> + <property name="text"> + <string>G</string> + </property> + </action> + <action name="actionSetB"> + <property name="text"> + <string>B</string> + </property> + </action> + <action name="actionSetNIR"> + <property name="text"> + <string>nIR</string> + </property> + </action> + <action name="actionSetSWIR"> + <property name="text"> + <string>swIR</string> + </property> + </action> + <action name="actionSetDefaultSB"> + <property name="text"> + <string>Def</string> + </property> + </action> + </widget> + <resources> + <include location="resources.qrc"/> + </resources> + <connections> + <connection> + <sender>cbRenderType</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>stackedWidget</receiver> + <slot>setCurrentIndex(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>107</x> + <y>19</y> + </hint> + <hint type="destinationlabel"> + <x>144</x> + <y>54</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py index be586fa73831bbfdd3cc1633711d49aa48b58112..7571f86ca654cfcf440daa348e989bd9248f470f 100644 --- a/timeseriesviewer/utils.py +++ b/timeseriesviewer/utils.py @@ -20,7 +20,7 @@ """ # noinspection PyPep8Naming -import os, sys, math, re, io +import os, sys, math, re, io, fnmatch from collections import defaultdict @@ -58,6 +58,30 @@ def qgisInstance(): return None +def file_search(rootdir, pattern, recursive=False, ignoreCase=False): + assert os.path.isdir(rootdir), "Path is not a directory:{}".format(rootdir) + regType = type(re.compile('.*')) + results = [] + + for root, dirs, files in os.walk(rootdir): + for file in files: + if isinstance(pattern, regType): + if pattern.search(file): + path = os.path.join(root, file) + results.append(path) + + elif (ignoreCase and fnmatch.fnmatch(file.lower(), pattern.lower())) \ + or fnmatch.fnmatch(file, pattern): + + path = os.path.join(root, file) + results.append(path) + if not recursive: + break + pass + + return results + + def appendItemsToMenu(menu, itemsToAdd): """ Appends items to QMenu "menu" @@ -555,6 +579,184 @@ class KeepRefs(object): +def defaultBands(dataset): + """ + Returns a list of 3 default bands + :param dataset: + :return: + """ + if isinstance(dataset, str): + return defaultBands(gdal.Open(dataset)) + elif isinstance(dataset, QgsRasterDataProvider): + return defaultBands(dataset.dataSourceUri()) + elif isinstance(dataset, QgsRasterLayer): + return defaultBands(dataset.source()) + elif isinstance(dataset, gdal.Dataset): + + db = dataset.GetMetadataItem(str('default_bands'), str('ENVI')) + if db != None: + db = [int(n) for n in re.findall('\d+')] + return db + db = [0, 0, 0] + cis = [gdal.GCI_RedBand, gdal.GCI_GreenBand, gdal.GCI_BlueBand] + for b in range(dataset.RasterCount): + band = dataset.GetRasterBand(b + 1) + assert isinstance(band, gdal.Band) + ci = band.GetColorInterpretation() + if ci in cis: + db[cis.index(ci)] = b + if db != [0, 0, 0]: + return db + + rl = QgsRasterLayer(dataset.GetFileList()[0]) + defaultRenderer = rl.renderer() + if isinstance(defaultRenderer, QgsRasterRenderer): + db = defaultRenderer.usesBands() + if len(db) == 0: + return [0, 1, 2] + if len(db) > 3: + db = db[0:3] + db = [b-1 for b in db] + return db + + else: + raise Exception() + +######### Lookup tables +METRIC_EXPONENTS = { + "nm": -9, "um": -6, u"µm": -6, "mm": -3, "cm": -2, "dm": -1, "m": 0, "hm": 2, "km": 3 +} +# add synonyms +METRIC_EXPONENTS['nanometers'] = METRIC_EXPONENTS['nm'] +METRIC_EXPONENTS['micrometers'] = METRIC_EXPONENTS['um'] +METRIC_EXPONENTS['millimeters'] = METRIC_EXPONENTS['mm'] +METRIC_EXPONENTS['centimeters'] = METRIC_EXPONENTS['cm'] +METRIC_EXPONENTS['decimeters'] = METRIC_EXPONENTS['dm'] +METRIC_EXPONENTS['meters'] = METRIC_EXPONENTS['m'] +METRIC_EXPONENTS['hectometers'] = METRIC_EXPONENTS['hm'] +METRIC_EXPONENTS['kilometers'] = METRIC_EXPONENTS['km'] + + +LUT_WAVELENGTH = dict({'B': 480, + 'G': 570, + 'R': 660, + 'NIR': 850, + 'SWIR': 1650, + 'SWIR1': 1650, + 'SWIR2': 2150 + }) + +def convertMetricUnit(value, u1, u2): + """converts value, given in unit u1, to u2""" + assert u1 in METRIC_EXPONENTS.keys() + assert u2 in METRIC_EXPONENTS.keys() + + e1 = METRIC_EXPONENTS[u1] + e2 = METRIC_EXPONENTS[u2] + + return value * 10 ** (e1 - e2) + +def bandClosestToWavelength(dataset, wl, wl_unit='nm'): + """ + Returns the band index (!) of an image dataset closest to wavelength `wl`. + :param dataset: str | gdal.Dataset + :param wl: wavelength to search the closed band for + :param wl_unit: unit of wavelength. Default = nm + :return: band index | 0 of wavelength information is not provided + """ + if isinstance(wl, str): + assert wl.upper() in LUT_WAVELENGTH.keys(), wl + return bandClosestToWavelength(dataset, LUT_WAVELENGTH[wl.upper()], wl_unit='nm') + else: + try: + wl = float(wl) + ds_wl, ds_wlu = parseWavelength(dataset) + + if ds_wl is None or ds_wlu is None: + return 0 + + + if ds_wlu != wl_unit: + wl = convertMetricUnit(wl, wl_unit, ds_wlu) + return int(np.argmin(np.abs(ds_wl - wl))) + except: + pass + return 0 + +def cloneRenderer(renderer): + + assert isinstance(renderer, QgsRasterRenderer) + cloned = renderer.clone() + + #handle specific issues if cloning is not exactly the same + if isinstance(cloned, QgsSingleBandPseudoColorRenderer): + cloned.setClassificationMin(renderer.classificationMin()) + cloned.setClassificationMax(renderer.classificationMax()) + + return cloned + + +def parseWavelength(dataset): + """ + Returns the wavelength + wavelength unit of a dataset + :param dataset: + :return: (wl, wl_u) or (None, None), if not existing + """ + + wl = None + wlu = None + + if isinstance(dataset, str): + return parseWavelength(gdal.Open(dataset)) + elif isinstance(dataset, QgsRasterDataProvider): + return parseWavelength(dataset.dataSourceUri()) + elif isinstance(dataset, QgsRasterLayer): + return parseWavelength(dataset.source()) + elif isinstance(dataset, gdal.Dataset): + + for domain in dataset.GetMetadataDomainList(): + # see http://www.harrisgeospatial.com/docs/ENVIHeaderFiles.html for supported wavelength units + + mdDict = dataset.GetMetadata_Dict(domain) + + for key, values in mdDict.items(): + key = key.lower() + if re.search('wavelength$', key, re.I): + tmp = re.findall('\d*\.\d+|\d+', values) # find floats + if len(tmp) != dataset.RasterCount: + tmp = re.findall('\d+', values) # find integers + if len(tmp) == dataset.RasterCount: + wl = np.asarray([float(w) for w in tmp]) + + if re.search(r'wavelength.units?', key): + if re.search('(Micrometers?|um)', values, re.I): + wlu = 'um' # fix with python 3 UTF + elif re.search('(Nanometers?|nm)', values, re.I): + wlu = 'nm' + elif re.search('(Millimeters?|mm)', values, re.I): + wlu = 'nm' + elif re.search('(Centimeters?|cm)', values, re.I): + wlu = 'nm' + elif re.search('(Meters?|m)', values, re.I): + wlu = 'nm' + elif re.search('Wavenumber', values, re.I): + wlu = '-' + elif re.search('GHz', values, re.I): + wlu = 'GHz' + elif re.search('MHz', values, re.I): + wlu = 'MHz' + elif re.search('Index', values, re.I): + wlu = '-' + else: + wlu = '-' + + if wl is not None and len(wl) > dataset.RasterCount: + wl = wl[0:dataset.RasterCount] + + return wl, wlu + + + def filterSubLayers(filePaths, subLayerEndings): """ Returns sub layers endings from all gdal Datasets within filePaths @@ -580,6 +782,7 @@ def filterSubLayers(filePaths, subLayerEndings): pass return results + def copyRenderer(renderer, targetLayer): """ Copies and applies renderer to targetLayer. @@ -587,25 +790,14 @@ def copyRenderer(renderer, targetLayer): :param targetLayer: :return: True, if 'renderer' could be copied and applied to 'targetLayer' """ + from timeseriesviewer.mapvisualization import cloneRenderer if isinstance(targetLayer, QgsRasterLayer) and isinstance(renderer, QgsRasterRenderer): - if isinstance(renderer, QgsMultiBandColorRenderer): - r = renderer.clone() - r.setInput(targetLayer.dataProvider()) - targetLayer.setRenderer(r) - return True - elif isinstance(renderer, QgsSingleBandPseudoColorRenderer): - r = renderer.clone() - # r = QgsSingleBandPseudoColorRenderer(None, renderer.band(), None) - r.setInput(targetLayer.dataProvider()) - cmin = renderer.classificationMin() - cmax = renderer.classificationMax() - r.setClassificationMin(cmin) - r.setClassificationMax(cmax) - targetLayer.setRenderer(r) - return True + + targetLayer.setRenderer(cloneRenderer(renderer)) + return True elif isinstance(targetLayer, QgsVectorLayer) and isinstance(renderer, QgsFeatureRenderer): - #todo: add render-specific switches - targetLayer.setRenderer(renderer) + + targetLayer.setRenderer(cloneRenderer(renderer)) return True return False