Skip to content

Commit

Permalink
Merge pull request #3885 from alexbruy/processing-relief
Browse files Browse the repository at this point in the history
[processing] add missed functionality from Raster Terrain Analysis plugin
alexbruy authored Dec 19, 2016
2 parents f9db068 + f4f86ca commit 4ecad0a
Showing 19 changed files with 585 additions and 1,228 deletions.
1 change: 0 additions & 1 deletion debian/qgis.install
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ usr/lib/qgis/plugins/libinterpolationplugin.so
usr/lib/qgis/plugins/libcoordinatecaptureplugin.so
usr/lib/qgis/plugins/liboracleplugin.so
usr/lib/qgis/plugins/libevis.so
usr/lib/qgis/plugins/librasterterrainplugin.so
usr/lib/qgis/plugins/libspatialqueryplugin.so
usr/lib/qgis/plugins/libofflineeditingplugin.so
usr/lib/qgis/plugins/libtopolplugin.so
1 change: 0 additions & 1 deletion ms-windows/osgeo4w/package.cmd
Original file line number Diff line number Diff line change
@@ -388,7 +388,6 @@ tar -C %OSGEO4W_ROOT% -cjf %ARCH%/release/qgis/%PACKAGENAME%/%PACKAGENAME%-%VERS
"apps/%PACKAGENAME%/plugins/interpolationplugin.dll" ^
"apps/%PACKAGENAME%/plugins/offlineeditingplugin.dll" ^
"apps/%PACKAGENAME%/plugins/oracleplugin.dll" ^
"apps/%PACKAGENAME%/plugins/rasterterrainplugin.dll" ^
"apps/%PACKAGENAME%/plugins/spatialqueryplugin.dll" ^
"apps/%PACKAGENAME%/plugins/topolplugin.dll" ^
"apps/%PACKAGENAME%/plugins/geometrycheckerplugin.dll" ^
1 change: 0 additions & 1 deletion ms-windows/plugins.nsh
Original file line number Diff line number Diff line change
@@ -17,7 +17,6 @@ WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "grassplugin" "true"
WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "interpolationplugin" "true"
WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "offlineeditingplugin" "true"
WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "oracleplugin" "true"
WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "rasterterrainplugin" "true"
WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "spatialqueryplugin" "true"
WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "topolplugin" "true"

4 changes: 2 additions & 2 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Original file line number Diff line number Diff line change
@@ -166,7 +166,7 @@
from .Slope import Slope
from .Ruggedness import Ruggedness
from .Hillshade import Hillshade
from .ReliefAuto import ReliefAuto
from .Relief import Relief
from .IdwInterpolationZValue import IdwInterpolationZValue
from .IdwInterpolationAttribute import IdwInterpolationAttribute
from .TinInterpolationZValue import TinInterpolationZValue
@@ -247,7 +247,7 @@ def __init__(self):
OffsetLine(), PolygonCentroids(), Translate(),
SingleSidedBuffer(), PointsAlongGeometry(),
Aspect(), Slope(), Ruggedness(), Hillshade(),
ReliefAuto(), ZonalStatisticsQgis(),
Relief(), ZonalStatisticsQgis(),
IdwInterpolationZValue(), IdwInterpolationAttribute(),
TinInterpolationZValue(), TinInterpolationAttribute(),
RemoveNullGeometry(), ExtractByExpression(),
161 changes: 161 additions & 0 deletions python/plugins/processing/algs/qgis/Relief.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-

"""
***************************************************************************
Relief.py
---------------------
Date : December 2016
Copyright : (C) 2016 by Alexander Bruy
Email : alexander dot bruy at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""

__author__ = 'Alexander Bruy'
__date__ = 'December 2016'
__copyright__ = '(C) 2016, Alexander Bruy'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os

from qgis.PyQt.QtGui import QIcon, QColor

from qgis.analysis import QgsRelief

from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import (Parameter,
ParameterRaster,
ParameterNumber,
ParameterBoolean,
_splitParameterOptions)
from processing.core.outputs import OutputRaster, OutputTable
from processing.tools import raster

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


class Relief(GeoAlgorithm):

INPUT_LAYER = 'INPUT_LAYER'
Z_FACTOR = 'Z_FACTOR'
AUTO_COLORS = 'AUTO_COLORS'
COLORS = 'COLORS'
OUTPUT_LAYER = 'OUTPUT_LAYER'
FREQUENCY_DISTRIBUTION = 'FREQUENCY_DISTRIBUTION'

def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'dem.png'))

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Relief')
self.group, self.i18n_group = self.trAlgorithm('Raster terrain analysis')

class ParameterReliefColors(Parameter):
default_metadata = {
'widget_wrapper': 'processing.algs.qgis.ui.ReliefColorsWidget.ReliefColorsWidgetWrapper'
}

def __init__(self, name='', description='', parent=None, optional=True):
Parameter.__init__(self, name, description, None, optional)
self.parent = parent

def setValue(self, value):
if value is None:
if not self.optional:
return False
self.value = None
return True

if value == '':
if not self.optional:
return False

if isinstance(value, str):
self.value = value if value != '' else None
else:
self.value = ParameterReliefColors.colorsToString(value)
return True

def getValueAsCommandLineParameter(self):
return '"{}"'.format(self.value)

def getAsScriptCode(self):
param_type = ''
param_type += 'relief colors '
return '##' + self.name + '=' + param_type

@classmethod
def fromScriptCode(self, line):
isOptional, name, definition = _splitParameterOptions(line)
descName = _createDescriptiveName(name)
parent = definition.lower().strip()[len('relief colors') + 1:]
return ParameterReliefColors(name, description, parent)

@staticmethod
def colorsToString(colors):
s = ''
for c in colors:
s += '{:f}, {:f}, {:d}, {:d}, {:d};'.format(c[0],
c[1],
c[2],
c[3],
c[4])
return s[:-1]

self.addParameter(ParameterRaster(self.INPUT_LAYER,
self.tr('Elevation layer')))
self.addParameter(ParameterNumber(self.Z_FACTOR,
self.tr('Z factor'),
1.0, 999999.99, 1.0))
self.addParameter(ParameterBoolean(self.AUTO_COLORS,
self.tr('Generate relief classes automaticaly'),
False))
self.addParameter(ParameterReliefColors(self.COLORS,
self.tr('Relief colors'),
self.INPUT_LAYER,
True))
self.addOutput(OutputRaster(self.OUTPUT_LAYER,
self.tr('Relief')))
self.addOutput(OutputTable(self.FREQUENCY_DISTRIBUTION,
self.tr('Frequency distribution')))

def processAlgorithm(self, progress):
inputFile = self.getParameterValue(self.INPUT_LAYER)
zFactor = self.getParameterValue(self.Z_FACTOR)
automaticColors = self.getParameterValue(self.AUTO_COLORS)
colors = self.getParameterValue(self.COLORS)
outputFile = self.getOutputValue(self.OUTPUT_LAYER)
frequencyDistribution = self.getOutputValue(self.FREQUENCY_DISTRIBUTION)

outputFormat = raster.formatShortNameFromFileName(outputFile)

relief = QgsRelief(inputFile, outputFile, outputFormat)

if automaticColors:
reliefColors = relief.calculateOptimizedReliefClasses()
else:
if colors is None:
raise GeoAlgorithmExecutionException(
self.tr('Specify relief colors or activate "Generate relief classes automaticaly" option.'))

reliefColors = []
for c in colors.split(';'):
v = c.split(',')
color = QgsRelief.ReliefColor(QColor(int(v[2]), int(v[3]), int(v[4])),
float(v[0]),
float(v[1]))
reliefColors.append(color)

relief.setReliefColors(reliefColors)
relief.setZFactor(zFactor)
relief.exportFrequencyDistributionToCsv(frequencyDistribution)
relief.processRaster(None)
80 changes: 0 additions & 80 deletions python/plugins/processing/algs/qgis/ReliefAuto.py

This file was deleted.

274 changes: 274 additions & 0 deletions python/plugins/processing/algs/qgis/ui/ReliefColorsWidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
# -*- coding: utf-8 -*-

"""
***************************************************************************
ReliefColorsWidget.py
---------------------
Date : December 2016
Copyright : (C) 2016 by Alexander Bruy
Email : alexander dot bruy at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""

__author__ = 'Alexander Bruy'
__date__ = 'December 2016'
__copyright__ = '(C) 2016, Alexander Bruy'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
import codecs

from qgis.PyQt import uic
from qgis.PyQt.QtCore import pyqtSlot, QDir
from qgis.PyQt.QtGui import (QIcon,
QBrush,
QColor)
from qgis.PyQt.QtWidgets import (QTreeWidgetItem,
QFileDialog,
QMessageBox,
QInputDialog,
QColorDialog
)
from qgis.PyQt.QtXml import QDomDocument

from qgis.core import QgsApplication, QgsMapLayer
from qgis.analysis import QgsRelief

from processing.gui.wrappers import WidgetWrapper
from processing.tools import system

pluginPath = os.path.dirname(__file__)
WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'reliefcolorswidgetbase.ui'))


class ReliefColorsWidget(BASE, WIDGET):

def __init__(self):
super(ReliefColorsWidget, self).__init__(None)
self.setupUi(self)

self.btnAdd.setIcon(QgsApplication.getThemeIcon('/symbologyAdd.svg'))
self.btnRemove.setIcon(QgsApplication.getThemeIcon('/symbologyRemove.svg'))
self.btnUp.setIcon(QgsApplication.getThemeIcon('/symbologyUp.svg'))
self.btnDown.setIcon(QgsApplication.getThemeIcon('/symbologyDown.svg'))
self.btnLoad.setIcon(QgsApplication.getThemeIcon('/mActionFileOpen.svg'))
self.btnSave.setIcon(QgsApplication.getThemeIcon('/mActionFileSave.svg'))
self.btnAuto.setIcon(QgsApplication.getThemeIcon('/mActionDraw.svg'))

self.layer = None

@pyqtSlot()
def on_btnAdd_clicked(self):
item = QTreeWidgetItem()
item.setText(0, '0.00')
item.setText(1, '0.00')
item.setBackground(2, QBrush(QColor(127, 127, 127)))
self.reliefClassTree.addTopLevelItem(item)

@pyqtSlot()
def on_btnRemove_clicked(self):
selectedItems = self.reliefClassTree.selectedItems()
for item in selectedItems:
self.reliefClassTree.invisibleRootItem().removeChild(item)
item = None

@pyqtSlot()
def on_btnDown_clicked(self):
selectedItems = self.reliefClassTree.selectedItems()
for item in selectedItems:
currentIndex = self.reliefClassTree.indexOfTopLevelItem(item)
if currentIndex < self.reliefClassTree.topLevelItemCount() - 1:
self.reliefClassTree.takeTopLevelItem(currentIndex)
self.reliefClassTree.insertTopLevelItem(currentIndex + 1, item)
self.reliefClassTree.setCurrentItem(item)

@pyqtSlot()
def on_btnUp_clicked(self):
selectedItems = self.reliefClassTree.selectedItems()
for item in selectedItems:
currentIndex = self.reliefClassTree.indexOfTopLevelItem(item)
if currentIndex > 0:
self.reliefClassTree.takeTopLevelItem(currentIndex)
self.reliefClassTree.insertTopLevelItem(currentIndex - 1, item)
self.reliefClassTree.setCurrentItem(item)

@pyqtSlot()
def on_btnLoad_clicked(self):
fileName, _ = QFileDialog.getOpenFileName(None,
self.tr('Import Colors and elevations from XML'),
QDir.homePath(),
self.tr('XML files (*.xml *.XML)'))
if fileName == '':
return

doc = QDomDocument()
with codecs.open(fileName, 'r', encoding='utf-8') as f:
content = f.read()

if not doc.setContent(content):
QMessageBox.critical(None,
self.tr('Error parsing XML'),
self.tr('The XML file could not be loaded'))
return

self.reliefClassTree.clear()
reliefColorList = doc.elementsByTagName('ReliefColor')
for i in range(reliefColorList.length()):
elem = reliefColorList.at(i).toElement()
item = QTreeWidgetItem()
item.setText(0, elem.attribute('MinElevation'))
item.setText(1, elem.attribute('MaxElevation'))
item.setBackground(2, QBrush(QColor(int(elem.attribute('red')),
int(elem.attribute('green')),
int(elem.attribute('blue')))))
self.reliefClassTree.addTopLevelItem(item)

@pyqtSlot()
def on_btnSave_clicked(self):
fileName, _ = QFileDialog.getSaveFileName(None,
self.tr('Export Colors and elevations as XML'),
QDir.homePath(),
self.tr('XML files (*.xml *.XML)'))

if fileName == '':
return

if not fileName.lower().endswith('.xml'):
fileName += '.xml'

doc = QDomDocument()
colorsElem = doc.createElement('ReliefColors')
doc.appendChild(colorsElem)

colors = self.reliefColors()
for c in colors:
elem = doc.createElement('ReliefColor')
elem.setAttribute('MinElevation', str(c.minElevation))
elem.setAttribute('MaxElevation', str(c.maxElevation))
elem.setAttribute('red', str(c.color.red()))
elem.setAttribute('green', str(c.color.green()))
elem.setAttribute('blue', str(c.color.blue()))
colorsElem.appendChild(elem)

with codecs.open(fileName, 'w', encoding='utf-8') as f:
f.write(doc.toString(2))

@pyqtSlot()
def on_btnAuto_clicked(self):
if self.layer is None:
return

relief = QgsRelief(self.layer, system.getTempFilename(), 'GTiff')
colors = relief.calculateOptimizedReliefClasses()
self.populateColors(colors)

@pyqtSlot(QTreeWidgetItem, int)
def on_reliefClassTree_itemDoubleClicked(self, item, column):
if not item:
return

if column == 0:
d, ok = QInputDialog.getDouble(None,
self.tr('Enter lower elevation class bound'),
self.tr('Elevation'),
float(item.text(0)),
decimals=2)
if ok:
item.setText(0, str(d))
elif column == 1:
d, ok = QInputDialog.getDouble(None,
self.tr('Enter upper elevation class bound'),
self.tr('Elevation'),
float(item.text(1)),
decimals=2)
if ok:
item.setText(1, str(d))
elif column == 2:
c = QColorDialog.getColor(item.background(2).color(),
None,
self.tr('Select color for relief class'))
if c.isValid():
item.setBackground(2, QBrush(c))

def reliefColors(self):
colors = []
for i in range(self.reliefClassTree.topLevelItemCount()):
item = self.reliefClassTree.topLevelItem(i)
if item:
c = QgsRelief.ReliefColor(item.background(2).color(),
float(item.text(0)),
float(item.text(1)))
colors.append(c)
return colors

def populateColors(self, colors):
self.reliefClassTree.clear()
for c in colors:
item = QTreeWidgetItem()
item.setText(0, str(c.minElevation))
item.setText(1, str(c.maxElevation))
item.setBackground(2, QBrush(c.color))
self.reliefClassTree.addTopLevelItem(item)

def setLayer(self, layer):
self.layer = layer

def setValue(self, value):
self.reliefClassTree.clear()
rows = value.split(';')
for r in rows:
v = r.split(',')
item = QTreeWidgetItem()
item.setText(0, v[0])
item.setText(1, v[1])
color = QColor(int(v[2]), int(v[3]), int(v[4]))
item.setBackground(2, QBrush(color))
self.reliefClassTree.addTopLevelItem(item)

def value(self):
rColors = self.reliefColors()
colors = ''
for c in rColors:
colors += '{:f}, {:f}, {:d}, {:d}, {:d};'.format(c.minElevation,
c.maxElevation,
c.color.red(),
c.color.green(),
c.color.blue())
return colors[:-1]


class ReliefColorsWidgetWrapper(WidgetWrapper):

def createWidget(self):
return ReliefColorsWidget()

def postInitialize(self, wrappers):
for wrapper in wrappers:
if wrapper.param.name == self.param.parent:
self.setLayer(wrapper.value())
wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
break

def parentValueChanged(self, wrapper):
self.setLayer(wrapper.value())

def setLayer(self, layer):
if isinstance(layer, QgsMapLayer):
layer = layer.source()
self.widget.setLayer(layer)

def setValue(self, value):
self.widget.setValue(value)

def value(self):
return self.widget.value()
127 changes: 127 additions & 0 deletions python/plugins/processing/algs/qgis/ui/reliefcolorswidgetbase.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>505</width>
<height>209</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTreeWidget" name="reliefClassTree">
<column>
<property name="text">
<string>Lower bound</string>
</property>
</column>
<column>
<property name="text">
<string>Upper bound</string>
</property>
</column>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolButton" name="btnAdd">
<property name="toolTip">
<string>Add row</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnRemove">
<property name="toolTip">
<string>Remove row</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnUp">
<property name="toolTip">
<string>Move up</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnDown">
<property name="toolTip">
<string>Move down</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="btnLoad">
<property name="toolTip">
<string>Load colors from file</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnSave">
<property name="toolTip">
<string>Save colors to file</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnAuto">
<property name="toolTip">
<string>Generate color table automatically</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
23 changes: 21 additions & 2 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Original file line number Diff line number Diff line change
@@ -1136,9 +1136,28 @@ tests:
hash: 58365b3715b925d6286e7f082ebd9c2a20f09fa1c922176d3f238002
type: rasterhash

- algorithm: qgis:reliefautomaticcolors
name: Relief (automatic colors calculation)
- algorithm: qgis:relief
name: Relief (automatic colors generation)
params:
AUTO_COLORS: true
INPUT_LAYER:
name: dem.tif
type: raster
Z_FACTOR: 1.0
results:
OUTPUT_LAYER:
hash: 7fe0e0174185fd743e23760f33615adf10f771b4275f320db6f7f4f8
type: rasterhash

- algorithm: qgis:relief
name: Relief (custom colors)
params:
AUTO_COLORS: false
COLORS: 85.000000, 104.436508, 7, 165, 144;104.436508, 104.436508, 12, 221, 162;104.436508,
104.436508, 33, 252, 183;104.436508, 104.436508, 247, 252, 152;104.436508, 104.436508,
252, 196, 8;104.436508, 190.333333, 252, 166, 15;190.333333, 226.698413, 175,
101, 15;226.698413, 226.698413, 255, 133, 92;226.698413, 243.000000, 204, 204,
204
INPUT_LAYER:
name: dem.tif
type: raster
1 change: 0 additions & 1 deletion src/plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ SET (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${QGIS_OUTPUT_DIRECTORY}/${QGIS_PLUGIN_SUBDI

ADD_SUBDIRECTORY(interpolation)
ADD_SUBDIRECTORY(oracle_raster)
ADD_SUBDIRECTORY(raster_terrain_analysis)
ADD_SUBDIRECTORY(coordinate_capture)
ADD_SUBDIRECTORY(evis)
ADD_SUBDIRECTORY(spatialquery)
62 changes: 0 additions & 62 deletions src/plugins/raster_terrain_analysis/CMakeLists.txt

This file was deleted.

Binary file removed src/plugins/raster_terrain_analysis/dem.png
Binary file not shown.
421 changes: 0 additions & 421 deletions src/plugins/raster_terrain_analysis/qgsrasterterrainanalysisdialog.cpp

This file was deleted.

This file was deleted.

This file was deleted.

255 changes: 0 additions & 255 deletions src/plugins/raster_terrain_analysis/qgsrasterterrainanalysisplugin.cpp

This file was deleted.

This file was deleted.

18 changes: 0 additions & 18 deletions src/plugins/raster_terrain_analysis/qgsruggednessindex.cpp

This file was deleted.

5 changes: 0 additions & 5 deletions src/plugins/raster_terrain_analysis/resources.qrc

This file was deleted.

0 comments on commit 4ecad0a

Please sign in to comment.