1# -*- coding: utf-8 -*-
2
3"""
4***************************************************************************
5    ReliefColorsWidget.py
6    ---------------------
7    Date                 : December 2016
8    Copyright            : (C) 2016 by Alexander Bruy
9    Email                : alexander dot bruy at gmail dot com
10***************************************************************************
11*                                                                         *
12*   This program is free software; you can redistribute it and/or modify  *
13*   it under the terms of the GNU General Public License as published by  *
14*   the Free Software Foundation; either version 2 of the License, or     *
15*   (at your option) any later version.                                   *
16*                                                                         *
17***************************************************************************
18"""
19
20__author__ = 'Alexander Bruy'
21__date__ = 'December 2016'
22__copyright__ = '(C) 2016, Alexander Bruy'
23
24import os
25import codecs
26
27from qgis.PyQt import uic
28from qgis.PyQt.QtCore import pyqtSlot, QDir
29from qgis.PyQt.QtGui import QColor, QBrush
30from qgis.PyQt.QtWidgets import (QTreeWidgetItem,
31                                 QFileDialog,
32                                 QMessageBox,
33                                 QInputDialog,
34                                 QColorDialog
35                                 )
36from qgis.PyQt.QtXml import QDomDocument
37
38from qgis.core import QgsApplication, QgsMapLayer
39from qgis.analysis import QgsRelief
40
41from processing.gui.wrappers import WidgetWrapper
42from processing.tools import system
43
44pluginPath = os.path.dirname(__file__)
45WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'reliefcolorswidgetbase.ui'))
46
47
48class ReliefColorsWidget(BASE, WIDGET):
49
50    def __init__(self):
51        super(ReliefColorsWidget, self).__init__(None)
52        self.setupUi(self)
53
54        self.btnAdd.setIcon(QgsApplication.getThemeIcon('/symbologyAdd.svg'))
55        self.btnRemove.setIcon(QgsApplication.getThemeIcon('/symbologyRemove.svg'))
56        self.btnUp.setIcon(QgsApplication.getThemeIcon('/mActionArrowUp.svg'))
57        self.btnDown.setIcon(QgsApplication.getThemeIcon('/mActionArrowDown.svg'))
58        self.btnLoad.setIcon(QgsApplication.getThemeIcon('/mActionFileOpen.svg'))
59        self.btnSave.setIcon(QgsApplication.getThemeIcon('/mActionFileSave.svg'))
60        self.btnAuto.setIcon(QgsApplication.getThemeIcon('/mActionReload.svg'))
61
62        self.layer = None
63
64    @pyqtSlot()
65    def on_btnAdd_clicked(self):
66        item = QTreeWidgetItem()
67        item.setText(0, '0.00')
68        item.setText(1, '0.00')
69        item.setBackground(2, QBrush(QColor(127, 127, 127)))
70        self.reliefClassTree.addTopLevelItem(item)
71
72    @pyqtSlot()
73    def on_btnRemove_clicked(self):
74        selectedItems = self.reliefClassTree.selectedItems()
75        for item in selectedItems:
76            self.reliefClassTree.invisibleRootItem().removeChild(item)
77            item = None
78
79    @pyqtSlot()
80    def on_btnDown_clicked(self):
81        selectedItems = self.reliefClassTree.selectedItems()
82        for item in selectedItems:
83            currentIndex = self.reliefClassTree.indexOfTopLevelItem(item)
84            if currentIndex < self.reliefClassTree.topLevelItemCount() - 1:
85                self.reliefClassTree.takeTopLevelItem(currentIndex)
86                self.reliefClassTree.insertTopLevelItem(currentIndex + 1, item)
87                self.reliefClassTree.setCurrentItem(item)
88
89    @pyqtSlot()
90    def on_btnUp_clicked(self):
91        selectedItems = self.reliefClassTree.selectedItems()
92        for item in selectedItems:
93            currentIndex = self.reliefClassTree.indexOfTopLevelItem(item)
94            if currentIndex > 0:
95                self.reliefClassTree.takeTopLevelItem(currentIndex)
96                self.reliefClassTree.insertTopLevelItem(currentIndex - 1, item)
97                self.reliefClassTree.setCurrentItem(item)
98
99    @pyqtSlot()
100    def on_btnLoad_clicked(self):
101        fileName, _ = QFileDialog.getOpenFileName(None,
102                                                  self.tr('Import Colors and elevations from XML'),
103                                                  QDir.homePath(),
104                                                  self.tr('XML files (*.xml *.XML)'))
105        if fileName == '':
106            return
107
108        doc = QDomDocument()
109        with codecs.open(fileName, 'r', encoding='utf-8') as f:
110            content = f.read()
111
112        if not doc.setContent(content):
113            QMessageBox.critical(None,
114                                 self.tr('Error parsing XML'),
115                                 self.tr('The XML file could not be loaded'))
116            return
117
118        self.reliefClassTree.clear()
119        reliefColorList = doc.elementsByTagName('ReliefColor')
120        for i in range(reliefColorList.length()):
121            elem = reliefColorList.at(i).toElement()
122            item = QTreeWidgetItem()
123            item.setText(0, elem.attribute('MinElevation'))
124            item.setText(1, elem.attribute('MaxElevation'))
125            item.setBackground(2, QBrush(QColor(int(elem.attribute('red')),
126                                                int(elem.attribute('green')),
127                                                int(elem.attribute('blue')))))
128            self.reliefClassTree.addTopLevelItem(item)
129
130    @pyqtSlot()
131    def on_btnSave_clicked(self):
132        fileName, _ = QFileDialog.getSaveFileName(None,
133                                                  self.tr('Export Colors and elevations as XML'),
134                                                  QDir.homePath(),
135                                                  self.tr('XML files (*.xml *.XML)'))
136
137        if fileName == '':
138            return
139
140        if not fileName.lower().endswith('.xml'):
141            fileName += '.xml'
142
143        doc = QDomDocument()
144        colorsElem = doc.createElement('ReliefColors')
145        doc.appendChild(colorsElem)
146
147        colors = self.reliefColors()
148        for c in colors:
149            elem = doc.createElement('ReliefColor')
150            elem.setAttribute('MinElevation', str(c.minElevation))
151            elem.setAttribute('MaxElevation', str(c.maxElevation))
152            elem.setAttribute('red', str(c.color.red()))
153            elem.setAttribute('green', str(c.color.green()))
154            elem.setAttribute('blue', str(c.color.blue()))
155            colorsElem.appendChild(elem)
156
157        with codecs.open(fileName, 'w', encoding='utf-8') as f:
158            f.write(doc.toString(2))
159
160    @pyqtSlot()
161    def on_btnAuto_clicked(self):
162        if self.layer is None:
163            return
164
165        relief = QgsRelief(self.layer, system.getTempFilename(), 'GTiff')
166        colors = relief.calculateOptimizedReliefClasses()
167        self.populateColors(colors)
168
169    @pyqtSlot(QTreeWidgetItem, int)
170    def on_reliefClassTree_itemDoubleClicked(self, item, column):
171        if not item:
172            return
173
174        if column == 0:
175            d, ok = QInputDialog.getDouble(None,
176                                           self.tr('Enter lower elevation class bound'),
177                                           self.tr('Elevation'),
178                                           float(item.text(0)),
179                                           decimals=2)
180            if ok:
181                item.setText(0, str(d))
182        elif column == 1:
183            d, ok = QInputDialog.getDouble(None,
184                                           self.tr('Enter upper elevation class bound'),
185                                           self.tr('Elevation'),
186                                           float(item.text(1)),
187                                           decimals=2)
188            if ok:
189                item.setText(1, str(d))
190        elif column == 2:
191            c = QColorDialog.getColor(item.background(2).color(),
192                                      None,
193                                      self.tr('Select color for relief class'))
194            if c.isValid():
195                item.setBackground(2, QBrush(c))
196
197    def reliefColors(self):
198        colors = []
199        for i in range(self.reliefClassTree.topLevelItemCount()):
200            item = self.reliefClassTree.topLevelItem(i)
201            if item:
202                c = QgsRelief.ReliefColor(item.background(2).color(),
203                                          float(item.text(0)),
204                                          float(item.text(1)))
205                colors.append(c)
206        return colors
207
208    def populateColors(self, colors):
209        self.reliefClassTree.clear()
210        for c in colors:
211            item = QTreeWidgetItem()
212            item.setText(0, str(c.minElevation))
213            item.setText(1, str(c.maxElevation))
214            item.setBackground(2, QBrush(c.color))
215            self.reliefClassTree.addTopLevelItem(item)
216
217    def setLayer(self, layer):
218        self.layer = layer
219
220    def setValue(self, value):
221        self.reliefClassTree.clear()
222        rows = value.split(';')
223        for r in rows:
224            v = r.split(',')
225            item = QTreeWidgetItem()
226            item.setText(0, v[0])
227            item.setText(1, v[1])
228            color = QColor(int(v[2]), int(v[3]), int(v[4]))
229            item.setBackground(2, QBrush(color))
230            self.reliefClassTree.addTopLevelItem(item)
231
232    def value(self):
233        rColors = self.reliefColors()
234        colors = ''
235        for c in rColors:
236            colors += '{:f}, {:f}, {:d}, {:d}, {:d};'.format(c.minElevation,
237                                                             c.maxElevation,
238                                                             c.color.red(),
239                                                             c.color.green(),
240                                                             c.color.blue())
241        return colors[:-1]
242
243
244class ReliefColorsWidgetWrapper(WidgetWrapper):
245
246    def createWidget(self):
247        return ReliefColorsWidget()
248
249    def postInitialize(self, wrappers):
250        for wrapper in wrappers:
251            if wrapper.param.name == self.param.parent:
252                self.setLayer(wrapper.value())
253                wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
254                break
255
256    def parentValueChanged(self, wrapper):
257        self.setLayer(wrapper.parameterValue())
258
259    def setLayer(self, layer):
260        if isinstance(layer, QgsMapLayer):
261            layer = layer.source()
262        self.widget.setLayer(layer)
263
264    def setValue(self, value):
265        self.widget.setValue(value)
266
267    def value(self):
268        return self.widget.value()
269