├── processing_provider ├── __init__.py ├── provider.py ├── rvt_slrm.py ├── rvt_slope.py ├── rvt_hillshade.py ├── rvt_multi_hillshade.py ├── rvt_msrm.py ├── rvt_svf.py ├── rvt_local_dom.py ├── rvt_opns.py ├── rvt_asvf.py ├── rvt_sky_illum.py ├── rvt_fill_no_data.py ├── rvt_blender.py └── rvt_mstp.py ├── test ├── tenbytenraster.keywords ├── __init__.py ├── tenbytenraster.prj ├── tenbytenraster.asc ├── tenbytenraster.asc.aux.xml ├── tenbytenraster.lic ├── test_resources.py ├── tenbytenraster.qml ├── test_qrvt_dialog.py ├── test_translations.py ├── utilities.py ├── test_init.py ├── test_qgis_environment.py └── qgis_interface.py ├── icon.png ├── loading.gif ├── resources.qrc ├── remove_before_zip.bat ├── resources_bat.bat ├── scripts ├── compile-strings.sh ├── run-env-linux.sh └── update-strings.sh ├── i18n └── af.ts ├── rvt └── __init__.py ├── RELEASE_README.md ├── settings ├── blender_VAT.json ├── blender_file_example.json ├── default_terrains_settings.json └── default_blender_combinations.json ├── __init__.py ├── qrvt_dialog.py ├── README.md ├── metadata.txt ├── .gitignore ├── pb_tool.cfg ├── plugin_upload.py ├── resources.py ├── Makefile ├── pylintrc ├── LICENSE └── qrvt_dialog_about.py /processing_provider/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/tenbytenraster.keywords: -------------------------------------------------------------------------------- 1 | title: Tenbytenraster 2 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EarthObservation/rvt-qgis/HEAD/icon.png -------------------------------------------------------------------------------- /loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EarthObservation/rvt-qgis/HEAD/loading.gif -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # import qgis libs so that ve set the correct sip api version 2 | import qgis # pylint: disable=W0611 # NOQA -------------------------------------------------------------------------------- /resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/tenbytenraster.prj: -------------------------------------------------------------------------------- 1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] -------------------------------------------------------------------------------- /remove_before_zip.bat: -------------------------------------------------------------------------------- 1 | rmdir /s /q .\__pycache__\ 2>nul 2 | 3 | rmdir /s /q .\processing_provider\__pycache__\ 2>nul 4 | 5 | rmdir /s /q .\rvt\__pycache__\ 2>nul 6 | 7 | del ".\settings\plugin_size.json" >nul 2>&1 8 | pause -------------------------------------------------------------------------------- /resources_bat.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call "C:\Program Files\QGIS 3.0\bin\o4w_env.bat" 3 | call "C:\Program Files\QGIS 3.0\bin\qt5_env.bat" 4 | call "C:\Program Files\QGIS 3.0\bin\py3_env.bat" 5 | 6 | @echo on 7 | pyrcc5 -o resources.py resources.qrc -------------------------------------------------------------------------------- /scripts/compile-strings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | LRELEASE=$1 3 | LOCALES=$2 4 | 5 | 6 | for LOCALE in ${LOCALES} 7 | do 8 | echo "Processing: ${LOCALE}.ts" 9 | # Note we don't use pylupdate with qt .pro file approach as it is flakey 10 | # about what is made available. 11 | $LRELEASE i18n/${LOCALE}.ts 12 | done 13 | -------------------------------------------------------------------------------- /i18n/af.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @default 5 | 6 | 7 | Good morning 8 | Goeie more 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/tenbytenraster.asc: -------------------------------------------------------------------------------- 1 | NCOLS 10 2 | NROWS 10 3 | XLLCENTER 1535380.000000 4 | YLLCENTER 5083260.000000 5 | DX 10 6 | DY 10 7 | NODATA_VALUE -9999 8 | 0 1 2 3 4 5 6 7 8 9 9 | 0 1 2 3 4 5 6 7 8 9 10 | 0 1 2 3 4 5 6 7 8 9 11 | 0 1 2 3 4 5 6 7 8 9 12 | 0 1 2 3 4 5 6 7 8 9 13 | 0 1 2 3 4 5 6 7 8 9 14 | 0 1 2 3 4 5 6 7 8 9 15 | 0 1 2 3 4 5 6 7 8 9 16 | 0 1 2 3 4 5 6 7 8 9 17 | 0 1 2 3 4 5 6 7 8 9 18 | CRS 19 | NOTES 20 | -------------------------------------------------------------------------------- /test/tenbytenraster.asc.aux.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Point 4 | 5 | 6 | 7 | 9 8 | 4.5 9 | 0 10 | 2.872281323269 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /rvt/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Relief Visualization Toolbox – python library 3 | 4 | Contains functions for computing the visualizations. 5 | 6 | Credits: 7 | Žiga Kokalj (ziga.kokalj@zrc-sazu.si) 8 | Krištof Oštir (kristof.ostir@fgg.uni-lj.si) 9 | Klemen Zakšek 10 | Klemen Čotar 11 | Maja Somrak 12 | Žiga Maroh 13 | Nejc Čož 14 | 15 | Copyright: 16 | 2010-2020 Research Centre of the Slovenian Academy of Sciences and Arts 17 | 2016-2020 University of Ljubljana, Faculty of Civil and Geodetic Engineering 18 | """ 19 | -------------------------------------------------------------------------------- /test/tenbytenraster.lic: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tim Sutton, Linfiniti Consulting CC 5 | 6 | 7 | 8 | tenbytenraster.asc 9 | 2700044251 10 | Yes 11 | Tim Sutton 12 | Tim Sutton (QGIS Source Tree) 13 | Tim Sutton 14 | This data is publicly available from QGIS Source Tree. The original 15 | file was created and contributed to QGIS by Tim Sutton. 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /scripts/run-env-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | QGIS_PREFIX_PATH=/usr/local/qgis-2.0 4 | if [ -n "$1" ]; then 5 | QGIS_PREFIX_PATH=$1 6 | fi 7 | 8 | echo ${QGIS_PREFIX_PATH} 9 | 10 | 11 | export QGIS_PREFIX_PATH=${QGIS_PREFIX_PATH} 12 | export QGIS_PATH=${QGIS_PREFIX_PATH} 13 | export LD_LIBRARY_PATH=${QGIS_PREFIX_PATH}/lib 14 | export PYTHONPATH=${QGIS_PREFIX_PATH}/share/qgis/python:${QGIS_PREFIX_PATH}/share/qgis/python/plugins:${PYTHONPATH} 15 | 16 | echo "QGIS PATH: $QGIS_PREFIX_PATH" 17 | export QGIS_DEBUG=0 18 | export QGIS_LOG_FILE=/tmp/inasafe/realtime/logs/qgis.log 19 | 20 | export PATH=${QGIS_PREFIX_PATH}/bin:$PATH 21 | 22 | echo "This script is intended to be sourced to set up your shell to" 23 | echo "use a QGIS 2.0 built in $QGIS_PREFIX_PATH" 24 | echo 25 | echo "To use it do:" 26 | echo "source $BASH_SOURCE /your/optional/install/path" 27 | echo 28 | echo "Then use the make file supplied here e.g. make guitest" 29 | -------------------------------------------------------------------------------- /RELEASE_README.md: -------------------------------------------------------------------------------- 1 | # Release of QGIS Plugin for Relief Visualization Toolbox 2 | 3 | Here is a guide how to upload (release) new version of the RVT QGIS plugin to the QGIS plugin repository. 4 | 5 | Required is pb_tool python library (pip install pb_tool) and 6 | Qt Creator software to change version in about dialogue (can also be done with txt editor). 7 | 8 | To release/upload changes (new version): 9 | * Change version in `metadata.txt`. 10 | * Open file `qrvt_dialog_about.ui` (about dialogue) with Qt Creator and change version. 11 | * Run `remove_before_zip.bat` to remove all pycache files. 12 | * Create new zip release file with pb_tool plugin: run `pbt zip` in root project directory (`rvt-qgis`). 13 | Newly created zip file `rvt-qgis.zip` will be created/overwritten in `zip_build` subdirectory. 14 | * Login to QGIS plugin repository (https://plugins.qgis.org/plugins/) then click `Upload a plugin` 15 | and upload newly created `rvt-qgis.zip` file. 16 | * Add changes to plugin changelog in `RVT_py` repository, file `qgis_releases.rst`. -------------------------------------------------------------------------------- /test/test_resources.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """Resources test. 3 | 4 | .. note:: This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | """ 10 | 11 | __author__ = 'ziga.kokalj@zrc-sazu.si' 12 | __date__ = '2020-10-12' 13 | __copyright__ = 'Copyright 2020, Research Centre of the Slovenian Academy of Sciences and Arts' 14 | 15 | import unittest 16 | 17 | from qgis.PyQt.QtGui import QIcon 18 | 19 | 20 | 21 | class QRVTDialogTest(unittest.TestCase): 22 | """Test rerources work.""" 23 | 24 | def setUp(self): 25 | """Runs before each test.""" 26 | pass 27 | 28 | def tearDown(self): 29 | """Runs after each test.""" 30 | pass 31 | 32 | def test_icon_png(self): 33 | """Test we can click OK.""" 34 | path = ':/plugins/QRVT/icon.png' 35 | icon = QIcon(path) 36 | self.assertFalse(icon.isNull()) 37 | 38 | if __name__ == "__main__": 39 | suite = unittest.makeSuite(QRVTResourcesTest) 40 | runner = unittest.TextTestRunner(verbosity=2) 41 | runner.run(suite) 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /settings/blender_VAT.json: -------------------------------------------------------------------------------- 1 | { 2 | "combination": { 3 | "name": "VAT - Archaeological", 4 | "layers": [ 5 | { 6 | "layer": "1", 7 | "visualization_method": "Sky-View Factor", 8 | "norm": "Value", 9 | "min": 0.7, 10 | "max": 1.0, 11 | "blend_mode": "Multiply", 12 | "opacity": 25 13 | }, 14 | { 15 | "layer": "2", 16 | "visualization_method": "Openness - Positive", 17 | "norm": "Value", 18 | "min": 68, 19 | "max": 93, 20 | "blend_mode": "Overlay", 21 | "opacity": 50 22 | }, 23 | { 24 | "layer": "3", 25 | "visualization_method": "Slope gradient", 26 | "norm": "Value", 27 | "min": 0, 28 | "max": 50, 29 | "blend_mode": "Luminosity", 30 | "opacity": 50 31 | }, 32 | { 33 | "layer": "4", 34 | "visualization_method": "Hillshade", 35 | "norm": "Value", 36 | "min": 0, 37 | "max": 1, 38 | "blend_mode": "Normal", 39 | "opacity": 100 40 | } 41 | ] 42 | } 43 | } -------------------------------------------------------------------------------- /settings/blender_file_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "combination": { 3 | "name": "VAT - Archaeological", 4 | "layers": [ 5 | { 6 | "layer": "1", 7 | "visualization_method": "Sky-View Factor", 8 | "norm": "Value", 9 | "min": 0.7, 10 | "max": 1.0, 11 | "blend_mode": "Multiply", 12 | "opacity": 25 13 | }, 14 | { 15 | "layer": "2", 16 | "visualization_method": "Openness - Positive", 17 | "norm": "Value", 18 | "min": 68, 19 | "max": 93, 20 | "blend_mode": "Overlay", 21 | "opacity": 50 22 | }, 23 | { 24 | "layer": "3", 25 | "visualization_method": "Slope gradient", 26 | "norm": "Value", 27 | "min": 0, 28 | "max": 50, 29 | "blend_mode": "Luminosity", 30 | "opacity": 50 31 | }, 32 | { 33 | "layer": "4", 34 | "visualization_method": "Hillshade", 35 | "norm": "Value", 36 | "min": 0, 37 | "max": 1, 38 | "blend_mode": "Normal", 39 | "opacity": 100 40 | }, 41 | { 42 | "layer": "5", 43 | "visualization_method": "None" 44 | } 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /scripts/update-strings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | LOCALES=$* 3 | 4 | # Get newest .py files so we don't update strings unnecessarily 5 | 6 | CHANGED_FILES=0 7 | PYTHON_FILES=`find . -regex ".*\(ui\|py\)$" -type f` 8 | for PYTHON_FILE in $PYTHON_FILES 9 | do 10 | CHANGED=$(stat -c %Y $PYTHON_FILE) 11 | if [ ${CHANGED} -gt ${CHANGED_FILES} ] 12 | then 13 | CHANGED_FILES=${CHANGED} 14 | fi 15 | done 16 | 17 | # Qt translation stuff 18 | # for .ts file 19 | UPDATE=false 20 | for LOCALE in ${LOCALES} 21 | do 22 | TRANSLATION_FILE="i18n/$LOCALE.ts" 23 | if [ ! -f ${TRANSLATION_FILE} ] 24 | then 25 | # Force translation string collection as we have a new language file 26 | touch ${TRANSLATION_FILE} 27 | UPDATE=true 28 | break 29 | fi 30 | 31 | MODIFICATION_TIME=$(stat -c %Y ${TRANSLATION_FILE}) 32 | if [ ${CHANGED_FILES} -gt ${MODIFICATION_TIME} ] 33 | then 34 | # Force translation string collection as a .py file has been updated 35 | UPDATE=true 36 | break 37 | fi 38 | done 39 | 40 | if [ ${UPDATE} == true ] 41 | # retrieve all python files 42 | then 43 | echo ${PYTHON_FILES} 44 | # update .ts 45 | echo "Please provide translations by editing the translation files below:" 46 | for LOCALE in ${LOCALES} 47 | do 48 | echo "i18n/"${LOCALE}".ts" 49 | # Note we don't use pylupdate with qt .pro file approach as it is flakey 50 | # about what is made available. 51 | pylupdate4 -noobsolete ${PYTHON_FILES} -ts i18n/${LOCALE}.ts 52 | done 53 | else 54 | echo "No need to edit any translation files (.ts) because no python files" 55 | echo "has been updated since the last update translation. " 56 | fi 57 | -------------------------------------------------------------------------------- /test/tenbytenraster.qml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 0 26 | 27 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | /*************************************************************************** 4 | QRVT 5 | A QGIS plugin 6 | RVT plugin lets you compute different visualizations from raster DEM. 7 | Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ 8 | ------------------- 9 | begin : 2020-10-12 10 | copyright : (C) 2020 by Research Centre of the Slovenian Academy of Sciences and Arts 11 | email : ziga.kokalj@zrc-sazu.si 12 | git sha : $Format:%H$ 13 | ***************************************************************************/ 14 | 15 | /*************************************************************************** 16 | * * 17 | * This program is free software; you can redistribute it and/or modify * 18 | * it under the terms of the GNU General Public License as published by * 19 | * the Free Software Foundation; either version 2 of the License, or * 20 | * (at your option) any later version. * 21 | * * 22 | ***************************************************************************/ 23 | This script initializes the plugin, making it known to QGIS. 24 | """ 25 | 26 | 27 | # noinspection PyPep8Naming 28 | def classFactory(iface): # pylint: disable=invalid-name 29 | """Load QRVT class from file QRVT. 30 | 31 | :param iface: A QGIS interface instance. 32 | :type iface: QgsInterface 33 | """ 34 | # 35 | from .qrvt import QRVT 36 | return QRVT(iface) 37 | -------------------------------------------------------------------------------- /test/test_qrvt_dialog.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """Dialog test. 3 | 4 | .. note:: This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | """ 10 | 11 | __author__ = 'ziga.kokalj@zrc-sazu.si' 12 | __date__ = '2020-10-12' 13 | __copyright__ = 'Copyright 2020, Research Centre of the Slovenian Academy of Sciences and Arts' 14 | 15 | import unittest 16 | 17 | from qgis.PyQt.QtGui import QDialogButtonBox, QDialog 18 | 19 | from qrvt_dialog import QRVTDialog 20 | 21 | from utilities import get_qgis_app 22 | QGIS_APP = get_qgis_app() 23 | 24 | 25 | class QRVTDialogTest(unittest.TestCase): 26 | """Test dialog works.""" 27 | 28 | def setUp(self): 29 | """Runs before each test.""" 30 | self.dialog = QRVTDialog(None) 31 | 32 | def tearDown(self): 33 | """Runs after each test.""" 34 | self.dialog = None 35 | 36 | def test_dialog_ok(self): 37 | """Test we can click OK.""" 38 | 39 | button = self.dialog.button_box.button(QDialogButtonBox.Ok) 40 | button.click() 41 | result = self.dialog.result() 42 | self.assertEqual(result, QDialog.Accepted) 43 | 44 | def test_dialog_cancel(self): 45 | """Test we can click cancel.""" 46 | button = self.dialog.button_box.button(QDialogButtonBox.Cancel) 47 | button.click() 48 | result = self.dialog.result() 49 | self.assertEqual(result, QDialog.Rejected) 50 | 51 | if __name__ == "__main__": 52 | suite = unittest.makeSuite(QRVTDialogTest) 53 | runner = unittest.TextTestRunner(verbosity=2) 54 | runner.run(suite) 55 | 56 | -------------------------------------------------------------------------------- /test/test_translations.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """Safe Translations Test. 3 | 4 | .. note:: This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | """ 10 | from .utilities import get_qgis_app 11 | 12 | __author__ = 'ismailsunni@yahoo.co.id' 13 | __date__ = '12/10/2011' 14 | __copyright__ = ('Copyright 2012, Australia Indonesia Facility for ' 15 | 'Disaster Reduction') 16 | import unittest 17 | import os 18 | 19 | from qgis.PyQt.QtCore import QCoreApplication, QTranslator 20 | 21 | QGIS_APP = get_qgis_app() 22 | 23 | 24 | class SafeTranslationsTest(unittest.TestCase): 25 | """Test translations work.""" 26 | 27 | def setUp(self): 28 | """Runs before each test.""" 29 | if 'LANG' in iter(os.environ.keys()): 30 | os.environ.__delitem__('LANG') 31 | 32 | def tearDown(self): 33 | """Runs after each test.""" 34 | if 'LANG' in iter(os.environ.keys()): 35 | os.environ.__delitem__('LANG') 36 | 37 | def test_qgis_translations(self): 38 | """Test that translations work.""" 39 | parent_path = os.path.join(__file__, os.path.pardir, os.path.pardir) 40 | dir_path = os.path.abspath(parent_path) 41 | file_path = os.path.join( 42 | dir_path, 'i18n', 'af.qm') 43 | translator = QTranslator() 44 | translator.load(file_path) 45 | QCoreApplication.installTranslator(translator) 46 | 47 | expected_message = 'Goeie more' 48 | real_message = QCoreApplication.translate("@default", 'Good morning') 49 | self.assertEqual(real_message, expected_message) 50 | 51 | 52 | if __name__ == "__main__": 53 | suite = unittest.makeSuite(SafeTranslationsTest) 54 | runner = unittest.TextTestRunner(verbosity=2) 55 | runner.run(suite) 56 | -------------------------------------------------------------------------------- /test/utilities.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """Common functionality used by regression tests.""" 3 | 4 | import sys 5 | import logging 6 | 7 | 8 | LOGGER = logging.getLogger('QGIS') 9 | QGIS_APP = None # Static variable used to hold hand to running QGIS app 10 | CANVAS = None 11 | PARENT = None 12 | IFACE = None 13 | 14 | 15 | def get_qgis_app(): 16 | """ Start one QGIS application to test against. 17 | 18 | :returns: Handle to QGIS app, canvas, iface and parent. If there are any 19 | errors the tuple members will be returned as None. 20 | :rtype: (QgsApplication, CANVAS, IFACE, PARENT) 21 | 22 | If QGIS is already running the handle to that app will be returned. 23 | """ 24 | 25 | try: 26 | from qgis.PyQt import QtGui, QtCore 27 | from qgis.core import QgsApplication 28 | from qgis.gui import QgsMapCanvas 29 | from .qgis_interface import QgisInterface 30 | except ImportError: 31 | return None, None, None, None 32 | 33 | global QGIS_APP # pylint: disable=W0603 34 | 35 | if QGIS_APP is None: 36 | gui_flag = True # All test will run qgis in gui mode 37 | #noinspection PyPep8Naming 38 | QGIS_APP = QgsApplication(sys.argv, gui_flag) 39 | # Make sure QGIS_PREFIX_PATH is set in your env if needed! 40 | QGIS_APP.initQgis() 41 | s = QGIS_APP.showSettings() 42 | LOGGER.debug(s) 43 | 44 | global PARENT # pylint: disable=W0603 45 | if PARENT is None: 46 | #noinspection PyPep8Naming 47 | PARENT = QtGui.QWidget() 48 | 49 | global CANVAS # pylint: disable=W0603 50 | if CANVAS is None: 51 | #noinspection PyPep8Naming 52 | CANVAS = QgsMapCanvas(PARENT) 53 | CANVAS.resize(QtCore.QSize(400, 400)) 54 | 55 | global IFACE # pylint: disable=W0603 56 | if IFACE is None: 57 | # QgisInterface is a stub implementation of the QGIS plugin interface 58 | #noinspection PyPep8Naming 59 | IFACE = QgisInterface(CANVAS) 60 | 61 | return QGIS_APP, CANVAS, IFACE, PARENT 62 | -------------------------------------------------------------------------------- /test/test_init.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """Tests QGIS plugin init.""" 3 | 4 | __author__ = 'Tim Sutton ' 5 | __revision__ = '$Format:%H$' 6 | __date__ = '17/10/2010' 7 | __license__ = "GPL" 8 | __copyright__ = 'Copyright 2012, Australia Indonesia Facility for ' 9 | __copyright__ += 'Disaster Reduction' 10 | 11 | import os 12 | import unittest 13 | import logging 14 | import configparser 15 | 16 | LOGGER = logging.getLogger('QGIS') 17 | 18 | 19 | class TestInit(unittest.TestCase): 20 | """Test that the plugin init is usable for QGIS. 21 | 22 | Based heavily on the validator class by Alessandro 23 | Passoti available here: 24 | 25 | http://github.com/qgis/qgis-django/blob/master/qgis-app/ 26 | plugins/validator.py 27 | 28 | """ 29 | 30 | def test_read_init(self): 31 | """Test that the plugin __init__ will validate on plugins.qgis.org.""" 32 | 33 | # You should update this list according to the latest in 34 | # https://github.com/qgis/qgis-django/blob/master/qgis-app/ 35 | # plugins/validator.py 36 | 37 | required_metadata = [ 38 | 'name', 39 | 'description', 40 | 'version', 41 | 'qgisMinimumVersion', 42 | 'email', 43 | 'author'] 44 | 45 | file_path = os.path.abspath(os.path.join( 46 | os.path.dirname(__file__), os.pardir, 47 | 'metadata.txt')) 48 | LOGGER.info(file_path) 49 | metadata = [] 50 | parser = configparser.ConfigParser() 51 | parser.optionxform = str 52 | parser.read(file_path) 53 | message = 'Cannot find a section named "general" in %s' % file_path 54 | assert parser.has_section('general'), message 55 | metadata.extend(parser.items('general')) 56 | 57 | for expectation in required_metadata: 58 | message = ('Cannot find metadata "%s" in metadata source (%s).' % ( 59 | expectation, file_path)) 60 | 61 | self.assertIn(expectation, dict(metadata), message) 62 | 63 | if __name__ == '__main__': 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /test/test_qgis_environment.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """Tests for QGIS functionality. 3 | 4 | 5 | .. note:: This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | """ 11 | __author__ = 'tim@linfiniti.com' 12 | __date__ = '20/01/2011' 13 | __copyright__ = ('Copyright 2012, Australia Indonesia Facility for ' 14 | 'Disaster Reduction') 15 | 16 | import os 17 | import unittest 18 | from qgis.core import ( 19 | QgsProviderRegistry, 20 | QgsCoordinateReferenceSystem, 21 | QgsRasterLayer) 22 | 23 | from .utilities import get_qgis_app 24 | QGIS_APP = get_qgis_app() 25 | 26 | 27 | class QGISTest(unittest.TestCase): 28 | """Test the QGIS Environment""" 29 | 30 | def test_qgis_environment(self): 31 | """QGIS environment has the expected providers""" 32 | 33 | r = QgsProviderRegistry.instance() 34 | self.assertIn('gdal', r.providerList()) 35 | self.assertIn('ogr', r.providerList()) 36 | self.assertIn('postgres', r.providerList()) 37 | 38 | def test_projection(self): 39 | """Test that QGIS properly parses a wkt string. 40 | """ 41 | crs = QgsCoordinateReferenceSystem() 42 | wkt = ( 43 | 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",' 44 | 'SPHEROID["WGS_1984",6378137.0,298.257223563]],' 45 | 'PRIMEM["Greenwich",0.0],UNIT["Degree",' 46 | '0.0174532925199433]]') 47 | crs.createFromWkt(wkt) 48 | auth_id = crs.authid() 49 | expected_auth_id = 'EPSG:4326' 50 | self.assertEqual(auth_id, expected_auth_id) 51 | 52 | # now test for a loaded layer 53 | path = os.path.join(os.path.dirname(__file__), 'tenbytenraster.asc') 54 | title = 'TestRaster' 55 | layer = QgsRasterLayer(path, title) 56 | auth_id = layer.crs().authid() 57 | self.assertEqual(auth_id, expected_auth_id) 58 | 59 | if __name__ == '__main__': 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /processing_provider/provider.py: -------------------------------------------------------------------------------- 1 | from qgis.core import QgsProcessingProvider 2 | 3 | from .rvt_hillshade import RVTHillshade 4 | from .rvt_multi_hillshade import RVTMultiHillshade 5 | from .rvt_slope import RVTSlope 6 | from .rvt_slrm import RVTSlrm 7 | from .rvt_svf import RVTSvf 8 | from .rvt_asvf import RVTASvf 9 | from .rvt_opns import RVTOpns 10 | from .rvt_sky_illum import RVTSim 11 | from .rvt_local_dom import RVTLocalDom 12 | from .rvt_blender import RVTBlender 13 | from .rvt_msrm import RVTMsrm 14 | from .rvt_mstp import RVTMstp 15 | from .rvt_fill_no_data import RVTFillNoData, RVTFillNoDataIDW 16 | 17 | 18 | class Provider(QgsProcessingProvider): 19 | def loadAlgorithms(self, *args, **kwargs): 20 | self.addAlgorithm(RVTHillshade()) 21 | self.addAlgorithm(RVTMultiHillshade()) 22 | self.addAlgorithm(RVTSlope()) 23 | self.addAlgorithm(RVTSlrm()) 24 | self.addAlgorithm(RVTSvf()) 25 | self.addAlgorithm(RVTASvf()) 26 | self.addAlgorithm(RVTOpns()) 27 | # self.addAlgorithm(RVTSim()) 28 | self.addAlgorithm(RVTLocalDom()) 29 | self.addAlgorithm(RVTBlender()) 30 | self.addAlgorithm(RVTMsrm()) 31 | self.addAlgorithm(RVTMstp()) 32 | self.addAlgorithm(RVTFillNoData()) 33 | self.addAlgorithm(RVTFillNoDataIDW()) 34 | 35 | def id(self, *args, **kwargs): 36 | """The ID of your plugin, used for identifying the provider. 37 | 38 | This string should be a unique, short, character only string, 39 | eg "qgis" or "gdal". This string should not be localised. 40 | """ 41 | return 'rvt' 42 | 43 | def name(self, *args, **kwargs): 44 | """The human friendly name of your plugin in Processing. 45 | 46 | This string should be as short as possible (e.g. "Lastools", not 47 | "Lastools version 1.0.1 64-bit") and localised. 48 | """ 49 | return self.tr('Relief visualization toolbox') 50 | 51 | def icon(self): 52 | """Should return a QIcon which is used for your provider inside 53 | the Processing toolbox. 54 | """ 55 | return QgsProcessingProvider.icon(self) 56 | -------------------------------------------------------------------------------- /qrvt_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | /*************************************************************************** 4 | QRVTDialog 5 | A QGIS plugin 6 | RVT plugin lets you compute different visualizations from raster DEM. 7 | Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ 8 | ------------------- 9 | begin : 2020-10-12 10 | git sha : $Format:%H$ 11 | copyright : (C) 2020 by Research Centre of the Slovenian Academy of Sciences and Arts 12 | email : ziga.kokalj@zrc-sazu.si 13 | ***************************************************************************/ 14 | 15 | /*************************************************************************** 16 | * * 17 | * This program is free software; you can redistribute it and/or modify * 18 | * it under the terms of the GNU General Public License as published by * 19 | * the Free Software Foundation; either version 2 of the License, or * 20 | * (at your option) any later version. * 21 | * * 22 | ***************************************************************************/ 23 | """ 24 | 25 | import os 26 | 27 | from qgis.PyQt import uic 28 | from qgis.PyQt import QtWidgets 29 | 30 | # This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer 31 | FORM_CLASS, _ = uic.loadUiType(os.path.join( 32 | os.path.dirname(__file__), 'qrvt_dialog_base.ui')) 33 | 34 | 35 | class QRVTDialog(QtWidgets.QDialog, FORM_CLASS): 36 | def __init__(self, parent=None): 37 | """Constructor.""" 38 | super(QRVTDialog, self).__init__(parent) 39 | # Set up the user interface from Designer through FORM_CLASS. 40 | # After self.setupUi() you can access any designer object by doing 41 | # self., and you can use autoconnect slots - see 42 | # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html 43 | # #widgets-and-dialogs-with-auto-connect 44 | self.setupUi(self) 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Relief Visualization Toolbox QGIS Plugin 2 | 3 | ![](./docs/figures/RVT_head.png) 4 | 5 | Relief Visualization Toolbox was produced to help scientist visualize raster elevation model datasets. We have narrowed down the selection to include techniques that have proven to be effective for identification of small scale features. Default settings therefore assume working with high resolution digital elevation models, derived from airborne laser scanning missions (lidar). 6 | 7 | 8 | ## Installation 9 | Plugin is uploaded to QGIS plugin repository [Relief visualization toolbox, QGIS plugin repository](https://plugins.qgis.org/plugins/rvt-qgis/). 10 | 11 | Detailed installation guide is available at [Relief visualization toolbox, QGIS Plugin installation](https://rvt-qgis.readthedocs.io/en/latest/install.html). 12 | 13 | To install plugin open QGIS and go to: 14 | 15 | ``` 16 | Plugins > Manage and install plugins... > Search Relief Visualization Toolbox > Install 17 | ``` 18 | 19 | The plugin has been tested under QGIS 3.12 and later. 20 | 21 | ## Documentation 22 | 23 | Documentation of the package and its usage is available at [Relief Visualization Toolbox in Python documentation](https://rvt-py.readthedocs.io/). 24 | 25 | ## References 26 | 27 | When using the tools, please cite: 28 | 29 | * Kokalj, Ž., Somrak, M. 2019. Why Not a Single Image? Combining Visualizations to Facilitate Fieldwork and On-Screen Mapping. Remote Sensing 11(7): 747. 30 | * Zakšek, K., Oštir, K., Kokalj, Ž. 2011. Sky-View Factor as a Relief Visualization Technique. Remote Sensing 3: 398-415. 31 | 32 | ## Contributing 33 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 34 | 35 | Please report any bugs and suggestions for improvements. 36 | 37 | ## Acknowledgment 38 | 39 | Development of RVT Python scripts was part financed by the Slovenian Research Agency core funding No. P2-0406 and by research projects No. J6-9395 and J2-9251. Development of RVT QGIS plugin was part financed by PTS Consultancy via the UK Government Culture Recovery Fund. 40 | 41 | ## License 42 | This project is licensed under the terms of the [Apache License](LICENSE). 43 | 44 | ## About 45 | RVT plugin for QGIS by Žiga Kokalj, Žiga Maroh, Nejc Čož and Krištof Oštir, 2022. 46 | It is developed in collaboration between ZRC SAZU and University of Ljubljana. 47 | -------------------------------------------------------------------------------- /metadata.txt: -------------------------------------------------------------------------------- 1 | # This file contains metadata for your plugin. 2 | 3 | # This file should be included when you package your plugin.# Mandatory items: 4 | 5 | [general] 6 | name=Relief Visualization Toolbox 7 | qgisMinimumVersion=3.0 8 | description=Relief visualization toolbox (RVT) plugin helps scientists visualize raster elevation model datasets. 9 | version=0.10.0 10 | author=ZRC SAZU and University of Ljubljana (UL FGG), Žiga Maroh 11 | email=ziga.maroh@icloud.com 12 | 13 | about=Relief Visualization Toolbox was developed to help scientist visualize raster elevation model datasets. We narrowed down the selection to include techniques that have proven to be effective for identification of small scale features. Default settings therefore assume working with high resolution digital elevation models, derived from airborne laser scanning missions (lidar). Despite this, techniques are also used for different other purposes. Sky-view factor, for example, can be efficiently used in numerous studies where digital elevation model visualizations and automatic feature extraction techniques are indispensable, e.g. in geography, geomorphology, cartography, hydrology, glaciology, forestry and disaster management. It can be used even in engineering applications, such as, predicting the availability of the GPS signal in urban areas. Methods currently implemented are: -hillshading, -hillshading from multiple directions, -slope gradient, -simple local relief model, -sky-view factor (as developed by our team), -anisotropic sky-view factor, -positive and negative openness, -sky illumination, and -local dominance. © Copyright 2020 ZRC SAZU and University of Ljubljana 14 | 15 | tracker=https://github.com/EarthObservation/rvt-qgis/issues 16 | repository=https://github.com/EarthObservation/rvt-qgis 17 | # End of mandatory metadata 18 | 19 | # Recommended items: 20 | 21 | hasProcessingProvider=yes 22 | # Uncomment the following line and add your changelog: 23 | changelog= 24 | [0.10.0] - 2025.09.19: 25 | * Updated to RVT version 2.2.3 26 | * Added e4MSTP to Blender combinations 27 | * Fixed minor bugs 28 | 29 | # Tags are comma separated with spaces allowed 30 | tags=python,rvt,relief_visualization_toolbox,relief_visualization,raster_visualization,visualization,dem_visualization 31 | 32 | homepage=https://rvt-py.readthedocs.io/en/latest/rvtfor_qgis.html 33 | category=Plugins 34 | icon=icon.png 35 | # experimental flag 36 | experimental=False 37 | 38 | # deprecated flag (applies to the whole plugin, not just a single version) 39 | deprecated=False 40 | 41 | # Since QGIS 3.8, a comma separated list of plugins to be installed 42 | # (or upgraded) can be specified. 43 | # Check the documentation for more information. 44 | # plugin_dependencies= 45 | 46 | Category of the plugin: Raster, Vector, Database or Web 47 | # category= 48 | 49 | # If the plugin can run on QGIS Server. 50 | server=False 51 | 52 | # LICENSE 53 | license=Apache-2.0 54 | license_file=LICENSE 55 | 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # Cython debug symbols 140 | cython_debug/ 141 | 142 | # VS Code 143 | .vscode/ 144 | 145 | settings/plugin_size.json 146 | /zip_build 147 | /.idea 148 | .idea 149 | settings/default_settings.json 150 | -------------------------------------------------------------------------------- /pb_tool.cfg: -------------------------------------------------------------------------------- 1 | #/*************************************************************************** 2 | # QRVT 3 | # 4 | # Configuration file for plugin builder tool (pb_tool) 5 | # Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ 6 | # ------------------- 7 | # begin : 2020-10-12 8 | # copyright : © Copyright 2020 ZRC SAZU and University of Ljubljana 9 | # email : ziga.kokalj@zrc-sazu.si 10 | # ***************************************************************************/ 11 | # 12 | #/*************************************************************************** 13 | # * * 14 | # * This program is free software; you can redistribute it and/or modify * 15 | # * it under the terms of the GNU General Public License as published by * 16 | # * the Free Software Foundation; either version 2 of the License, or * 17 | # * (at your option) any later version. * 18 | # * * 19 | # ***************************************************************************/ 20 | # 21 | # 22 | # You can install pb_tool using: 23 | # pip install http://geoapt.net/files/pb_tool.zip 24 | # 25 | # Consider doing your development (and install of pb_tool) in a virtualenv. 26 | # 27 | # For details on setting up and using pb_tool, see: 28 | # http://g-sherman.github.io/plugin_build_tool/ 29 | # 30 | # Issues and pull requests here: 31 | # https://github.com/g-sherman/plugin_build_tool: 32 | # 33 | # Sane defaults for your plugin generated by the Plugin Builder are 34 | # already set below. 35 | # 36 | # As you add Python source files and UI files to your plugin, add 37 | # them to the appropriate [files] section below. 38 | 39 | [plugin] 40 | # Name of the plugin. This is the name of the directory that will 41 | # be created in .qgis2/python/plugins 42 | name: rvt-qgis 43 | 44 | # Full path to where you want your plugin directory copied. If empty, 45 | # the QGIS default path will be used. Don't include the plugin name in 46 | # the path. 47 | plugin_path: 48 | 49 | [files] 50 | # Python files that should be deployed with the plugin 51 | python_files: __init__.py qrvt.py qrvt_dialog.py 52 | 53 | # The main dialog file that is loaded (not compiled) 54 | main_dialog: qrvt_dialog_base.ui qrvt_dialog_about.ui 55 | 56 | # Other ui files for dialogs you create (these will be compiled) 57 | compiled_ui_files: 58 | 59 | # Resource file(s) that will be compiled 60 | resource_files: resources.qrc 61 | 62 | # Other files required for the plugin 63 | extras: metadata.txt LICENSE icon.png loading.gif Makefile 64 | 65 | # Other directories to be deployed with the plugin. 66 | # These must be subdirectories under the plugin directory 67 | extra_dirs: rvt processing_provider settings i18n 68 | 69 | # ISO code(s) for any locales (translations), separated by spaces. 70 | # Corresponding .ts files must exist in the i18n directory 71 | locales: 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /settings/default_terrains_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terrains_settings": [ 3 | { 4 | "terrain_settings": { 5 | "name": "general", 6 | "Slope gradient": { 7 | "stretch": {"min": 0, "max": 50} 8 | }, 9 | "Hillshade": { 10 | "stretch": {"min": 0, "max": 1}, 11 | "hs_sun_el": {"value": 35} 12 | }, 13 | "Multiple directions hillshade": { 14 | "stretch": {"min": 0, "max": 1}, 15 | "mhs_sun_el": {"value": 35} 16 | }, 17 | "Simple local relief model": { 18 | "stretch": {"min": -1, "max": 1}, 19 | "slrm_rad_cell": {"value": 20} 20 | }, 21 | "Sky-View Factor": { 22 | "stretch": {"min": 0.7, "max": 1}, 23 | "svf_r_max": {"value": 10}, 24 | "svf_noise": {"value": 0} 25 | }, 26 | "Anisotropic Sky-View Factor": { 27 | "stretch": {"min": 0.65, "max": 1} 28 | }, 29 | "Openness - Positive": { 30 | "stretch": {"min": 68, "max": 93} 31 | }, 32 | "Openness - Negative": { 33 | "stretch": {"min": 60, "max": 95} 34 | }, 35 | "Sky illumination": { 36 | "stretch": {"min": 0.25, "max": 0.3} 37 | }, 38 | "Local dominance": { 39 | "stretch": {"min": 0.5, "max": 1.8}, 40 | "ld_min_rad": {"value": 10}, 41 | "ld_max_rad": {"value": 20}, 42 | "ld_observer_h": {"value": 1.7} 43 | } 44 | } 45 | 46 | }, 47 | { 48 | "terrain_settings": { 49 | "name": "flat", 50 | "Slope gradient": { 51 | "stretch": {"min": 0, "max": 15} 52 | }, 53 | "Hillshade": { 54 | "hs_sun_el": {"value": 15} 55 | }, 56 | "Multiple directions hillshade": { 57 | "mhs_sun_el": {"value": 15} 58 | }, 59 | "Simple local relief model": { 60 | "slrm_rad_cell": {"value": 10} 61 | }, 62 | "Sky-View Factor": { 63 | "stretch": {"min": 0.9, "max": 1}, 64 | "svf_r_max": {"value": 20}, 65 | "svf_noise": {"value": 3} 66 | }, 67 | "Openness - Positive": { 68 | "stretch": {"min": 85, "max": 93} 69 | }, 70 | "Openness - Negative": { 71 | "stretch": {"min": 75, "max": 95} 72 | }, 73 | "Local dominance": { 74 | "stretch": {"min": 0.5, "max": 3}, 75 | "ld_min_rad": {"value": 10}, 76 | "ld_max_rad": {"value": 20}, 77 | "ld_observer_h": {"value": 1.7} 78 | } 79 | } 80 | }, 81 | { 82 | "terrain_settings": { 83 | "name": "steep", 84 | "Slope gradient": { 85 | "stretch": {"min": 0, "max": 60} 86 | }, 87 | "Hillshade": { 88 | "hs_sun_el": {"value": 55} 89 | }, 90 | "Multiple directions hillshade": { 91 | "mhs_sun_el": {"value": 55} 92 | }, 93 | "Simple local relief model": { 94 | "slrm_rad_cell": {"value": 10} 95 | }, 96 | "Sky-View Factor": { 97 | "stretch": {"min": 0.55, "max": 1}, 98 | "svf_r_max": {"value": 10}, 99 | "svf_noise": {"value": 0} 100 | }, 101 | "Openness - Positive": { 102 | "stretch": {"min": 55, "max": 95} 103 | }, 104 | "Openness - Negative": { 105 | "stretch": {"min": 45, "max": 95} 106 | }, 107 | "Local dominance": { 108 | "stretch": {"min": 0.55, "max": 0.95}, 109 | "ld_min_rad": {"value": 10}, 110 | "ld_max_rad": {"value": 10}, 111 | "ld_observer_h": {"value": 16} 112 | } 113 | } 114 | 115 | } 116 | ] 117 | } -------------------------------------------------------------------------------- /plugin_upload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """This script uploads a plugin package to the plugin repository. 4 | Authors: A. Pasotti, V. Picavet 5 | git sha : $TemplateVCSFormat 6 | """ 7 | 8 | import sys 9 | import getpass 10 | import xmlrpc.client 11 | from optparse import OptionParser 12 | 13 | standard_library.install_aliases() 14 | 15 | # Configuration 16 | PROTOCOL = 'https' 17 | SERVER = 'plugins.qgis.org' 18 | PORT = '443' 19 | ENDPOINT = '/plugins/RPC2/' 20 | VERBOSE = False 21 | 22 | 23 | def main(parameters, arguments): 24 | """Main entry point. 25 | 26 | :param parameters: Command line parameters. 27 | :param arguments: Command line arguments. 28 | """ 29 | address = "{protocol}://{username}:{password}@{server}:{port}{endpoint}".format( 30 | protocol=PROTOCOL, 31 | username=parameters.username, 32 | password=parameters.password, 33 | server=parameters.server, 34 | port=parameters.port, 35 | endpoint=ENDPOINT) 36 | print("Connecting to: %s" % hide_password(address)) 37 | 38 | server = xmlrpc.client.ServerProxy(address, verbose=VERBOSE) 39 | 40 | try: 41 | with open(arguments[0], 'rb') as handle: 42 | plugin_id, version_id = server.plugin.upload( 43 | xmlrpc.client.Binary(handle.read())) 44 | print("Plugin ID: %s" % plugin_id) 45 | print("Version ID: %s" % version_id) 46 | except xmlrpc.client.ProtocolError as err: 47 | print("A protocol error occurred") 48 | print("URL: %s" % hide_password(err.url, 0)) 49 | print("HTTP/HTTPS headers: %s" % err.headers) 50 | print("Error code: %d" % err.errcode) 51 | print("Error message: %s" % err.errmsg) 52 | except xmlrpc.client.Fault as err: 53 | print("A fault occurred") 54 | print("Fault code: %d" % err.faultCode) 55 | print("Fault string: %s" % err.faultString) 56 | 57 | 58 | def hide_password(url, start=6): 59 | """Returns the http url with password part replaced with '*'. 60 | 61 | :param url: URL to upload the plugin to. 62 | :type url: str 63 | 64 | :param start: Position of start of password. 65 | :type start: int 66 | """ 67 | start_position = url.find(':', start) + 1 68 | end_position = url.find('@') 69 | return "%s%s%s" % ( 70 | url[:start_position], 71 | '*' * (end_position - start_position), 72 | url[end_position:]) 73 | 74 | 75 | if __name__ == "__main__": 76 | parser = OptionParser(usage="%prog [options] plugin.zip") 77 | parser.add_option( 78 | "-w", "--password", dest="password", 79 | help="Password for plugin site", metavar="******") 80 | parser.add_option( 81 | "-u", "--username", dest="username", 82 | help="Username of plugin site", metavar="user") 83 | parser.add_option( 84 | "-p", "--port", dest="port", 85 | help="Server port to connect to", metavar="80") 86 | parser.add_option( 87 | "-s", "--server", dest="server", 88 | help="Specify server name", metavar="plugins.qgis.org") 89 | options, args = parser.parse_args() 90 | if len(args) != 1: 91 | print("Please specify zip file.\n") 92 | parser.print_help() 93 | sys.exit(1) 94 | if not options.server: 95 | options.server = SERVER 96 | if not options.port: 97 | options.port = PORT 98 | if not options.username: 99 | # interactive mode 100 | username = getpass.getuser() 101 | print("Please enter user name [%s] :" % username, end=' ') 102 | 103 | res = input() 104 | if res != "": 105 | options.username = res 106 | else: 107 | options.username = username 108 | if not options.password: 109 | # interactive mode 110 | options.password = getpass.getpass() 111 | main(options, args) 112 | -------------------------------------------------------------------------------- /settings/default_blender_combinations.json: -------------------------------------------------------------------------------- 1 | { 2 | "combinations": [ 3 | { 4 | "combination": { 5 | "name": "Archaeological (VAT)", 6 | "layers": [ 7 | { 8 | "layer": "1", 9 | "visualization_method": "Sky-View Factor", 10 | "norm": "Value", 11 | "min": 0.7, 12 | "max": 1.0, 13 | "blend_mode": "Multiply", 14 | "opacity": 25 15 | }, 16 | { 17 | "layer": "2", 18 | "visualization_method": "Openness - Positive", 19 | "norm": "Value", 20 | "min": 68.0, 21 | "max": 93.0, 22 | "blend_mode": "Overlay", 23 | "opacity": 50 24 | }, 25 | { 26 | "layer": "3", 27 | "visualization_method": "Slope gradient", 28 | "norm": "Value", 29 | "min": 0.0, 30 | "max": 50.0, 31 | "blend_mode": "Luminosity", 32 | "opacity": 50 33 | }, 34 | { 35 | "layer": "4", 36 | "visualization_method": "Hillshade", 37 | "norm": "Value", 38 | "min": 0.0, 39 | "max": 1.0, 40 | "blend_mode": "Normal", 41 | "opacity": 100 42 | } 43 | ] 44 | } 45 | }, 46 | { 47 | "combination": { 48 | "name": "Archaeological combined (VAT combined)", 49 | "layers": [] 50 | } 51 | }, 52 | { 53 | "combination": { 54 | "name": "enhanced Multi-Scale Topographic Position version 3", 55 | "layers": [] 56 | } 57 | }, 58 | { 59 | "combination": { 60 | "name": "e4MSTP", 61 | "layers": [] 62 | } 63 | }, 64 | { 65 | "combination": { 66 | "name": "Prismatic openness", 67 | "layers": [ 68 | { 69 | "layer": "1", 70 | "visualization_method": "Openness - Positive", 71 | "norm": "Value", 72 | "min": 68.0, 73 | "max": 93.0, 74 | "blend_mode": "Overlay", 75 | "opacity": 50 76 | }, 77 | { 78 | "layer": "2", 79 | "visualization_method": "Openness - Negative", 80 | "norm": "Value", 81 | "min": 68.0, 82 | "max": 93.0, 83 | "blend_mode": "Overlay", 84 | "opacity": 50 85 | }, 86 | { 87 | "layer": "3", 88 | "visualization_method": "Multiple directions hillshade", 89 | "norm": "Value", 90 | "min": 0.0, 91 | "max": 1.0, 92 | "blend_mode": "Normal", 93 | "opacity": 100 94 | } 95 | ] 96 | } 97 | }, 98 | { 99 | "combination": { 100 | "name": "City", 101 | "layers": [ 102 | { 103 | "layer": "1", 104 | "visualization_method": "Shadow", 105 | "norm": "Value", 106 | "min": 0.0, 107 | "max": 1.0, 108 | "blend_mode": "Normal", 109 | "opacity": 70 110 | }, 111 | { 112 | "layer": "2", 113 | "visualization_method": "Sky-View Factor", 114 | "norm": "Value", 115 | "min": 0.1, 116 | "max": 1.0, 117 | "blend_mode": "Normal", 118 | "opacity": 10 119 | }, 120 | { 121 | "layer": "3", 122 | "visualization_method": "Sky illumination", 123 | "norm": "Perc", 124 | "min": 1.0, 125 | "max": 0.0, 126 | "blend_mode": "Normal", 127 | "opacity": 100 128 | } 129 | ] 130 | } 131 | } 132 | ] 133 | } -------------------------------------------------------------------------------- /processing_provider/rvt_slrm.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTSlrm(QgsProcessingAlgorithm): 19 | """ 20 | RVT Simple local relief model. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | RADIUS = "RADIUS" 26 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 27 | OUTPUT = 'OUTPUT' 28 | 29 | def tr(self, string): 30 | """ 31 | Returns a translatable string with the self.tr() function. 32 | """ 33 | return QCoreApplication.translate('Processing', string) 34 | 35 | def createInstance(self): 36 | return RVTSlrm() 37 | 38 | def name(self): 39 | """ 40 | Returns the algorithm name, used for identifying the algorithm. This 41 | string should be fixed for the algorithm, and must not be localised. 42 | The name should be unique within each provider. Names should contain 43 | lowercase alphanumeric characters only and no spaces or other 44 | formatting characters. 45 | """ 46 | return 'rvt_slrm' 47 | 48 | def displayName(self): 49 | """ 50 | Returns the translated algorithm name, which should be used for any 51 | user-visible display of the algorithm name. 52 | """ 53 | return self.tr('RVT Simplified local relief model') 54 | 55 | def shortHelpString(self): 56 | """ 57 | Returns a localised short helper string for the algorithm. This string 58 | should provide a basic description about what the algorithm does and the 59 | parameters and outputs associated with it.. 60 | """ 61 | return self.tr("Relief visualization toolbox, Simplified local relief model. Calculates Simplified local" 62 | " relief model.") 63 | 64 | def initAlgorithm(self, config=None): 65 | """ 66 | Here we define the inputs and output of the algorithm, along 67 | with some other properties. 68 | """ 69 | self.addParameter( 70 | QgsProcessingParameterRasterLayer( 71 | self.INPUT, 72 | self.tr('Input DEM raster layer'), 73 | [QgsProcessing.TypeRaster] 74 | ) 75 | ) 76 | self.addParameter( 77 | QgsProcessingParameterNumber( 78 | name="VE_FACTOR", 79 | description="Vertical exaggeration factor", 80 | type=QgsProcessingParameterNumber.Double, 81 | defaultValue=1, 82 | minValue=-1000, 83 | maxValue=1000 84 | ) 85 | ) 86 | self.addParameter( 87 | QgsProcessingParameterNumber( 88 | name="RADIUS", 89 | description="Radius for trend assessment [pixels]", 90 | type=QgsProcessingParameterNumber.Integer, 91 | defaultValue=20, 92 | minValue=10, 93 | maxValue=50 94 | ) 95 | ) 96 | self.addParameter( 97 | QgsProcessingParameterBoolean( 98 | name="SAVE_AS_8BIT", 99 | description="Save as 8bit raster", 100 | defaultValue=False 101 | ) 102 | ) 103 | self.addParameter( 104 | QgsProcessingParameterRasterDestination( 105 | self.OUTPUT, 106 | self.tr('Output visualization raster layer') 107 | ) 108 | ) 109 | 110 | def processAlgorithm(self, parameters, context, feedback): 111 | """ 112 | Here is where the processing itself takes place. 113 | """ 114 | dem_layer = self.parameterAsRasterLayer( 115 | parameters, 116 | self.INPUT, 117 | context 118 | ) 119 | 120 | ve_factor = float(self.parameterAsDouble( 121 | parameters, 122 | self.VE_FACTOR, 123 | context 124 | )) 125 | radius = int(self.parameterAsInt( 126 | parameters, 127 | self.RADIUS, 128 | context 129 | )) 130 | save_8bit = bool(self.parameterAsBool( 131 | parameters, 132 | self.SAVE_AS_8BIT, 133 | context 134 | )) 135 | visualization_path = (self.parameterAsOutputLayer( 136 | parameters, 137 | self.OUTPUT, 138 | context, 139 | )) 140 | 141 | dem_path = str(dem_layer.source()) 142 | 143 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 144 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 145 | dem_arr = dict_arr_dem["array"] 146 | no_data = dict_arr_dem["no_data"] 147 | 148 | visualization_arr = rvt.vis.slrm(dem=dem_arr, radius_cell=radius, ve_factor=ve_factor, no_data=no_data) 149 | if not save_8bit: 150 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 151 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 152 | else: 153 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 154 | float_arr=visualization_arr, visualization=RVTVisualization.SIMPLE_LOCAL_RELIEF_MODEL 155 | ) 156 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 157 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 158 | 159 | result = {self.OUTPUT: visualization_path} 160 | return result 161 | -------------------------------------------------------------------------------- /processing_provider/rvt_slope.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTSlope(QgsProcessingAlgorithm): 19 | """ 20 | RVT Slope gradient. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | UNIT = "UNIT" 26 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 27 | OUTPUT = 'OUTPUT' 28 | 29 | units_options = ["degree", "radian", "percent"] 30 | 31 | def tr(self, string): 32 | """ 33 | Returns a translatable string with the self.tr() function. 34 | """ 35 | return QCoreApplication.translate('Processing', string) 36 | 37 | def createInstance(self): 38 | return RVTSlope() 39 | 40 | def name(self): 41 | """ 42 | Returns the algorithm name, used for identifying the algorithm. This 43 | string should be fixed for the algorithm, and must not be localised. 44 | The name should be unique within each provider. Names should contain 45 | lowercase alphanumeric characters only and no spaces or other 46 | formatting characters. 47 | """ 48 | return 'rvt_slope' 49 | 50 | def displayName(self): 51 | """ 52 | Returns the translated algorithm name, which should be used for any 53 | user-visible display of the algorithm name. 54 | """ 55 | return self.tr('RVT Slope') 56 | 57 | def shortHelpString(self): 58 | """ 59 | Returns a localised short helper string for the algorithm. This string 60 | should provide a basic description about what the algorithm does and the 61 | parameters and outputs associated with it.. 62 | """ 63 | return self.tr("Relief visualization toolbox, Slope. Calculates slope gradient.") 64 | 65 | def initAlgorithm(self, config=None): 66 | """ 67 | Here we define the inputs and output of the algorithm, along 68 | with some other properties. 69 | """ 70 | self.addParameter( 71 | QgsProcessingParameterRasterLayer( 72 | self.INPUT, 73 | self.tr('Input DEM raster layer'), 74 | [QgsProcessing.TypeRaster] 75 | ) 76 | ) 77 | self.addParameter( 78 | QgsProcessingParameterNumber( 79 | name="VE_FACTOR", 80 | description="Vertical exaggeration factor", 81 | type=QgsProcessingParameterNumber.Double, 82 | defaultValue=1, 83 | minValue=-1000, 84 | maxValue=1000 85 | ) 86 | ) 87 | self.addParameter( 88 | QgsProcessingParameterEnum( 89 | name="UNIT", 90 | description="Output units", 91 | options=self.units_options, 92 | defaultValue="degree" 93 | ) 94 | ) 95 | self.addParameter( 96 | QgsProcessingParameterBoolean( 97 | name="SAVE_AS_8BIT", 98 | description="Save as 8bit raster", 99 | defaultValue=False 100 | ) 101 | ) 102 | self.addParameter( 103 | QgsProcessingParameterRasterDestination( 104 | self.OUTPUT, 105 | self.tr('Output visualization raster layer') 106 | ) 107 | ) 108 | 109 | def processAlgorithm(self, parameters, context, feedback): 110 | """ 111 | Here is where the processing itself takes place. 112 | """ 113 | dem_layer = self.parameterAsRasterLayer( 114 | parameters, 115 | self.INPUT, 116 | context 117 | ) 118 | 119 | ve_factor = float(self.parameterAsDouble( 120 | parameters, 121 | self.VE_FACTOR, 122 | context 123 | )) 124 | unit_enum = int(self.parameterAsEnum( 125 | parameters, 126 | self.UNIT, 127 | context 128 | )) 129 | unit = self.units_options[unit_enum] 130 | save_8bit = bool(self.parameterAsBool( 131 | parameters, 132 | self.SAVE_AS_8BIT, 133 | context 134 | )) 135 | visualization_path = (self.parameterAsOutputLayer( 136 | parameters, 137 | self.OUTPUT, 138 | context, 139 | )) 140 | 141 | dem_path = str(dem_layer.source()) 142 | 143 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 144 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 145 | dem_arr = dict_arr_dem["array"] 146 | no_data = dict_arr_dem["no_data"] 147 | 148 | visualization_arr = rvt.vis.slope_aspect(dem=dem_arr, resolution_x=resolution[0], resolution_y=resolution[1], 149 | output_units=unit, ve_factor=ve_factor, no_data=no_data)["slope"] 150 | if not save_8bit: 151 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 152 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 153 | else: 154 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 155 | float_arr=visualization_arr, visualization=RVTVisualization.SLOPE 156 | ) 157 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 158 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 159 | 160 | result = {self.OUTPUT: visualization_path} 161 | return result 162 | -------------------------------------------------------------------------------- /resources.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Resource object code 4 | # 5 | # Created by: The Resource Compiler for PyQt5 (Qt v5.15.1) 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore 10 | 11 | qt_resource_data = b"\ 12 | \x00\x00\x04\x0a\ 13 | \x89\ 14 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ 15 | \x00\x00\x17\x00\x00\x00\x18\x08\x06\x00\x00\x00\x11\x7c\x66\x75\ 16 | \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ 17 | \x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ 18 | \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\ 19 | \x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd9\x02\x15\ 20 | \x16\x11\x2c\x9d\x48\x83\xbb\x00\x00\x03\x8a\x49\x44\x41\x54\x48\ 21 | \xc7\xad\x95\x4b\x68\x5c\x55\x18\xc7\x7f\xe7\xdc\x7b\x67\xe6\xce\ 22 | \x4c\x66\x26\x49\xd3\x24\x26\xa6\xc6\xf8\x40\x21\xa5\x04\xb3\x28\ 23 | \xda\x98\x20\xa5\x0b\xad\x55\xa8\x2b\xc5\x50\x1f\xa0\x6e\x34\x2b\ 24 | \x45\x30\x14\x02\xba\x52\x69\x15\x17\x66\x63\x45\x97\x95\xa0\xad\ 25 | \x0b\xfb\xc0\x06\x25\xb6\x71\x61\x12\x41\x50\xdb\x2a\x21\xd1\xe2\ 26 | \x24\xf3\x9e\xc9\xcc\xbd\xe7\x1c\x17\x35\x43\x1e\x33\x21\xb6\xfd\ 27 | \x56\x87\xf3\x9d\xfb\xfb\x1e\xf7\xff\x9d\x23\x8c\x31\x43\x95\xf4\ 28 | \x85\x1e\x3f\x3b\x35\xac\xfd\xcc\x43\xdc\xa4\x49\x3b\xfe\x9d\x1d\ 29 | \xdb\x7b\x22\x90\x78\xf8\xb2\x28\xa7\xbe\x7d\xc1\x4b\x9d\x79\xdf\ 30 | \x18\x15\xe5\x16\x99\x10\x56\xde\x69\xdc\x3f\x22\xfd\xec\xd4\xf0\ 31 | \xad\x04\x03\x18\xa3\xa2\x7e\x76\x6a\x58\xde\x68\x2b\xb4\x36\xf8\ 32 | \xbe\xc6\x18\x53\xdb\xef\xe7\xfa\xec\xed\x67\x63\x10\x42\x00\xf0\ 33 | \xfb\xd5\x65\x2a\x15\x45\xc7\x6d\x0d\x00\xc4\xa2\xc1\xaa\x6f\x0d\ 34 | \x3e\x6c\xab\xc2\x1c\x56\xa4\x77\x4b\xb0\xf2\x35\x15\x5f\x21\x85\ 35 | \xe0\xc8\x6b\x5f\x92\x2d\x37\x33\x39\xf9\x03\x27\x8e\x1f\xa2\xf7\ 36 | \xbe\x9d\x04\x1c\x0b\x37\xe4\xac\xff\xa6\x30\x87\xbd\xba\x00\x6a\ 37 | \x06\x79\xe5\xf5\xaf\x89\xd9\x92\xc5\xcc\x0a\xd9\x7c\x19\xcf\xe9\ 38 | \xe2\xe4\xa9\x2f\x78\x7c\xff\x01\x72\x85\x0a\x2b\x65\x1f\xa5\x4c\ 39 | \xb5\xb2\x55\x16\x80\xbd\x31\xda\xda\x20\x1f\x7d\x3e\xcd\xc2\xfd\ 40 | \x59\xa6\x93\x39\x92\xd1\x22\xea\x9b\x16\xce\x9d\x3f\xce\xe0\x83\ 41 | \x03\x24\x82\x59\x3a\xdb\x7b\x88\xc7\x82\x68\x63\x58\xc9\xcc\x62\ 42 | \x8c\x21\x18\xb0\x6a\xc3\x37\x06\x49\x16\xff\x24\x6b\xa5\x49\xbb\ 43 | \x25\xbc\xa2\xa6\x21\xbb\x40\x7f\xdf\x00\x83\xbd\x01\x8e\x3c\xd5\ 44 | \x45\xd7\x8e\x6b\x9c\x9c\x98\x25\x1a\xb6\xe8\xbe\x3d\xc2\xdd\x77\ 45 | \x44\x48\xc4\x1c\x22\xe1\xeb\x58\x59\xaf\xcf\xd3\x33\x29\x2e\x34\ 46 | \x2d\x91\x93\x3e\xbe\x34\x78\x01\xc5\xe2\x61\xc5\xae\x72\x8e\x70\ 47 | \xc8\xc2\x0d\x5a\xbc\xf5\xee\x2f\x9c\xfa\x3e\x86\x69\x7a\x8e\xcf\ 48 | \x26\xe6\xf9\x63\xa1\x44\xa1\xa4\xd0\xda\x6c\x0d\x2f\x15\x7c\xb4\ 49 | \x67\x28\x59\x0a\xcf\xd6\x54\xe2\x06\x13\x87\x2b\x6f\x68\xa6\x27\ 50 | \xaf\x31\x32\x36\xc7\xb2\x7f\x17\xef\x7d\x7c\x8c\x33\x67\xcf\x12\ 51 | \x70\x24\x4a\x69\xd6\x6a\x46\xd6\xd3\x70\x72\xa9\x82\x67\x34\x45\ 52 | \xad\x28\xdb\x1a\x15\x34\x98\xff\x46\xed\xef\x37\x0d\x99\xbf\x4a\ 53 | \x3c\x30\x38\xc0\xc8\x4b\xaf\x92\x5a\x9c\xe2\xe0\x23\x6d\x74\xb4\ 54 | \xba\x84\x5d\x0b\x29\x45\x7d\xb8\x94\x82\x96\xb6\x10\xf3\xc5\x12\ 55 | \x2a\xef\x53\x11\x1a\x63\xad\x3f\x93\x19\x85\xf1\xb1\x77\x58\x5a\ 56 | \xf8\x99\x97\x9f\xe9\xa6\x75\x47\x90\xc6\xb8\x43\xd8\xb5\xb6\xce\ 57 | \xfc\xfa\xfd\x00\xfb\x3e\xf4\xc8\x05\x35\xba\x5e\xeb\x46\x21\xf9\ 58 | \xcf\x0a\xa9\x8c\x87\xe3\x48\xdc\x90\xb5\x6e\x98\x6a\xaa\x65\xf2\ 59 | \x52\x92\x43\x2f\x5e\xc2\x8c\x02\x1a\x10\xf5\x07\xac\xc3\x75\x70\ 60 | \x83\x92\x80\xb3\xf9\xd0\x26\xf8\x8f\xb3\x29\xc6\x3e\xb8\x8c\x19\ 61 | \x35\x75\x6b\x7b\x7e\x3c\xca\x45\x0c\x7e\x49\x31\xf4\x58\x3b\xf7\ 62 | \xf6\x34\x90\x88\x39\x04\x1c\x59\x1f\xfe\xdb\xd5\x3c\x5f\x9d\x4b\ 63 | \x32\xfd\x44\xb2\xba\xd7\xfa\xb6\x60\xcf\xde\x16\xdc\x90\x45\x4c\ 64 | \x4a\x2a\x9e\x62\xfe\x4e\xc5\xc8\xc1\x4e\xda\x76\x86\xe8\xe9\x0a\ 65 | \xe3\xd8\x92\x58\xd4\xc6\xb2\x44\x6d\x78\x2a\x53\xe1\xca\x7c\x99\ 66 | \x63\x5d\xbf\x56\x9d\xbd\x9f\x44\x18\x7a\xba\x95\x27\x0f\xb4\xd3\ 67 | \xdc\x18\xc0\xf3\x0d\x52\x40\xd8\xb5\xb0\xa4\x20\x14\xb2\x70\x6c\ 68 | \x81\x63\xcb\xaa\x42\xd6\xfd\xb7\xf4\xec\xa3\x06\xa0\x50\x52\xd8\ 69 | \x4e\x1b\x7e\x4a\xd3\x31\xf9\x29\xcf\xfe\xd4\x49\x7f\x5f\x13\xfb\ 70 | \xfa\x9b\x71\x43\x92\x58\xd4\x21\x18\x90\xac\xde\xb0\x42\x50\x13\ 71 | \x58\x33\xf3\x88\x6b\xa1\xfd\x65\x96\xf2\x79\xc6\x43\x7b\xd8\x75\ 72 | \x38\xcc\x3d\xdd\xd1\xaa\xcf\x71\xe4\xff\x7f\x91\x56\x33\xaf\xea\ 73 | \x37\xe7\xa1\x94\x21\x16\xb5\xd1\x06\x2c\x29\x36\xf5\x72\x9b\x96\ 74 | \x95\xc0\xc4\xda\x9d\x78\x83\x43\x53\x22\x80\x65\x09\x1c\xfb\x86\ 75 | \xc1\x00\xe7\x25\x70\x14\x48\x6f\x1e\x22\x51\xe3\x75\xd9\xb6\xa5\ 76 | \x81\xa3\x32\xb1\xfb\xf4\x0c\x30\xb8\xb1\x82\x9b\xb0\x09\x60\x30\ 77 | \xb1\xfb\xf4\xcc\xbf\xa0\xe9\x6e\xae\x5a\xdf\x4b\x81\x00\x00\x00\ 78 | \x00\x49\x45\x4e\x44\xae\x42\x60\x82\ 79 | " 80 | 81 | qt_resource_name = b"\ 82 | \x00\x07\ 83 | \x07\x3b\xe0\xb3\ 84 | \x00\x70\ 85 | \x00\x6c\x00\x75\x00\x67\x00\x69\x00\x6e\x00\x73\ 86 | \x00\x04\ 87 | \x00\x07\x89\xd4\ 88 | \x00\x71\ 89 | \x00\x72\x00\x76\x00\x74\ 90 | \x00\x08\ 91 | \x0a\x61\x5a\xa7\ 92 | \x00\x69\ 93 | \x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ 94 | " 95 | 96 | qt_resource_struct_v1 = b"\ 97 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 98 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 99 | \x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ 100 | \x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 101 | " 102 | 103 | qt_resource_struct_v2 = b"\ 104 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 105 | \x00\x00\x00\x00\x00\x00\x00\x00\ 106 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 107 | \x00\x00\x00\x00\x00\x00\x00\x00\ 108 | \x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ 109 | \x00\x00\x00\x00\x00\x00\x00\x00\ 110 | \x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 111 | \x00\x00\x01\x75\xb6\xde\xf1\x32\ 112 | " 113 | 114 | qt_version = [int(v) for v in QtCore.qVersion().split('.')] 115 | if qt_version < [5, 8, 0]: 116 | rcc_version = 1 117 | qt_resource_struct = qt_resource_struct_v1 118 | else: 119 | rcc_version = 2 120 | qt_resource_struct = qt_resource_struct_v2 121 | 122 | def qInitResources(): 123 | QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) 124 | 125 | def qCleanupResources(): 126 | QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) 127 | 128 | qInitResources() 129 | -------------------------------------------------------------------------------- /processing_provider/rvt_hillshade.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTHillshade(QgsProcessingAlgorithm): 19 | """ 20 | RVT Hillshade. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | SUN_AZIMUTH = 'SUN_AZIMUTH' 26 | SUN_ELEVATION = 'SUN_ELEVATION' 27 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 28 | OUTPUT = 'OUTPUT' 29 | 30 | def tr(self, string): 31 | """ 32 | Returns a translatable string with the self.tr() function. 33 | """ 34 | return QCoreApplication.translate('Processing', string) 35 | 36 | def createInstance(self): 37 | return RVTHillshade() 38 | 39 | def name(self): 40 | """ 41 | Returns the algorithm name, used for identifying the algorithm. This 42 | string should be fixed for the algorithm, and must not be localised. 43 | The name should be unique within each provider. Names should contain 44 | lowercase alphanumeric characters only and no spaces or other 45 | formatting characters. 46 | """ 47 | return 'rvt_hillshade' 48 | 49 | def displayName(self): 50 | """ 51 | Returns the translated algorithm name, which should be used for any 52 | user-visible display of the algorithm name. 53 | """ 54 | return self.tr('RVT Hillshade') 55 | 56 | def shortHelpString(self): 57 | """ 58 | Returns a localised short helper string for the algorithm. This string 59 | should provide a basic description about what the algorithm does and the 60 | parameters and outputs associated with it.. 61 | """ 62 | return self.tr("Relief visualization toolbox, Hillshade. Calculates hillshade.") 63 | 64 | def initAlgorithm(self, config=None): 65 | """ 66 | Here we define the inputs and output of the algorithm, along 67 | with some other properties. 68 | """ 69 | self.addParameter( 70 | QgsProcessingParameterRasterLayer( 71 | self.INPUT, 72 | self.tr('Input DEM raster layer'), 73 | [QgsProcessing.TypeRaster] 74 | ) 75 | ) 76 | self.addParameter( 77 | QgsProcessingParameterNumber( 78 | name="VE_FACTOR", 79 | description="Vertical exaggeration factor", 80 | type=QgsProcessingParameterNumber.Double, 81 | defaultValue=1, 82 | minValue=-1000, 83 | maxValue=1000 84 | ) 85 | ) 86 | self.addParameter( 87 | QgsProcessingParameterNumber( 88 | name="SUN_AZIMUTH", 89 | description="Solar azimuth angle (clockwise from North) in degrees", 90 | type=QgsProcessingParameterNumber.Double, 91 | defaultValue=315, 92 | minValue=0, 93 | maxValue=360 94 | ) 95 | ) 96 | self.addParameter( 97 | QgsProcessingParameterNumber( 98 | name="SUN_ELEVATION", 99 | description="Solar vertical angle (above the horizon) in degrees", 100 | type=QgsProcessingParameterNumber.Double, 101 | defaultValue=35, 102 | minValue=0, 103 | maxValue=90 104 | ) 105 | ) 106 | self.addParameter( 107 | QgsProcessingParameterBoolean( 108 | name="SAVE_AS_8BIT", 109 | description="Save as 8bit raster", 110 | defaultValue=False 111 | ) 112 | ) 113 | self.addParameter( 114 | QgsProcessingParameterRasterDestination( 115 | self.OUTPUT, 116 | self.tr('Output visualization raster layer') 117 | ) 118 | ) 119 | 120 | def processAlgorithm(self, parameters, context, feedback): 121 | """ 122 | Here is where the processing itself takes place. 123 | """ 124 | dem_layer = self.parameterAsRasterLayer( 125 | parameters, 126 | self.INPUT, 127 | context 128 | ) 129 | 130 | ve_factor = float(self.parameterAsDouble( 131 | parameters, 132 | self.VE_FACTOR, 133 | context 134 | )) 135 | sun_azimuth = float(self.parameterAsDouble( 136 | parameters, 137 | self.SUN_AZIMUTH, 138 | context 139 | )) 140 | sun_elevation = float(self.parameterAsDouble( 141 | parameters, 142 | self.SUN_ELEVATION, 143 | context 144 | )) 145 | save_8bit = bool(self.parameterAsBool( 146 | parameters, 147 | self.SAVE_AS_8BIT, 148 | context 149 | )) 150 | visualization_path = (self.parameterAsOutputLayer( 151 | parameters, 152 | self.OUTPUT, 153 | context, 154 | )) 155 | 156 | dem_path = str(dem_layer.source()) 157 | 158 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 159 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 160 | dem_arr = dict_arr_dem["array"] 161 | no_data = dict_arr_dem["no_data"] 162 | 163 | visualization_arr = rvt.vis.hillshade(dem=dem_arr, resolution_x=resolution[0], resolution_y=resolution[1], 164 | sun_azimuth=sun_azimuth, sun_elevation=sun_elevation, ve_factor=ve_factor, 165 | no_data=no_data) 166 | if not save_8bit: 167 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 168 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 169 | else: 170 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 171 | float_arr=visualization_arr, visualization=RVTVisualization.HILLSHADE 172 | ) 173 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 174 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 175 | 176 | result = {self.OUTPUT: visualization_path} 177 | return result 178 | 179 | -------------------------------------------------------------------------------- /test/qgis_interface.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """QGIS plugin implementation. 3 | 4 | .. note:: This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | .. note:: This source code was copied from the 'postgis viewer' application 10 | with original authors: 11 | Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk 12 | Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org 13 | Copyright (c) 2014 Tim Sutton, tim@linfiniti.com 14 | 15 | """ 16 | 17 | __author__ = 'tim@linfiniti.com' 18 | __revision__ = '$Format:%H$' 19 | __date__ = '10/01/2011' 20 | __copyright__ = ( 21 | 'Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and ' 22 | 'Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org' 23 | 'Copyright (c) 2014 Tim Sutton, tim@linfiniti.com' 24 | ) 25 | 26 | import logging 27 | from qgis.PyQt.QtCore import QObject, pyqtSlot, pyqtSignal 28 | from qgis.core import QgsMapLayerRegistry 29 | from qgis.gui import QgsMapCanvasLayer 30 | LOGGER = logging.getLogger('QGIS') 31 | 32 | 33 | #noinspection PyMethodMayBeStatic,PyPep8Naming 34 | class QgisInterface(QObject): 35 | """Class to expose QGIS objects and functions to plugins. 36 | 37 | This class is here for enabling us to run unit tests only, 38 | so most methods are simply stubs. 39 | """ 40 | currentLayerChanged = pyqtSignal(QgsMapCanvasLayer) 41 | 42 | def __init__(self, canvas): 43 | """Constructor 44 | :param canvas: 45 | """ 46 | QObject.__init__(self) 47 | self.canvas = canvas 48 | # Set up slots so we can mimic the behaviour of QGIS when layers 49 | # are added. 50 | LOGGER.debug('Initialising canvas...') 51 | # noinspection PyArgumentList 52 | QgsMapLayerRegistry.instance().layersAdded.connect(self.addLayers) 53 | # noinspection PyArgumentList 54 | QgsMapLayerRegistry.instance().layerWasAdded.connect(self.addLayer) 55 | # noinspection PyArgumentList 56 | QgsMapLayerRegistry.instance().removeAll.connect(self.removeAllLayers) 57 | 58 | # For processing module 59 | self.destCrs = None 60 | 61 | @pyqtSlot('QStringList') 62 | def addLayers(self, layers): 63 | """Handle layers being added to the registry so they show up in canvas. 64 | 65 | :param layers: list list of map layers that were added 66 | 67 | .. note:: The QgsInterface api does not include this method, 68 | it is added here as a helper to facilitate testing. 69 | """ 70 | #LOGGER.debug('addLayers called on qgis_interface') 71 | #LOGGER.debug('Number of layers being added: %s' % len(layers)) 72 | #LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers())) 73 | current_layers = self.canvas.layers() 74 | final_layers = [] 75 | for layer in current_layers: 76 | final_layers.append(QgsMapCanvasLayer(layer)) 77 | for layer in layers: 78 | final_layers.append(QgsMapCanvasLayer(layer)) 79 | 80 | self.canvas.setLayerSet(final_layers) 81 | #LOGGER.debug('Layer Count After: %s' % len(self.canvas.layers())) 82 | 83 | @pyqtSlot('QgsMapLayer') 84 | def addLayer(self, layer): 85 | """Handle a layer being added to the registry so it shows up in canvas. 86 | 87 | :param layer: list list of map layers that were added 88 | 89 | .. note: The QgsInterface api does not include this method, it is added 90 | here as a helper to facilitate testing. 91 | 92 | .. note: The addLayer method was deprecated in QGIS 1.8 so you should 93 | not need this method much. 94 | """ 95 | pass 96 | 97 | @pyqtSlot() 98 | def removeAllLayers(self): 99 | """Remove layers from the canvas before they get deleted.""" 100 | self.canvas.setLayerSet([]) 101 | 102 | def newProject(self): 103 | """Create new project.""" 104 | # noinspection PyArgumentList 105 | QgsMapLayerRegistry.instance().removeAllMapLayers() 106 | 107 | # ---------------- API Mock for QgsInterface follows ------------------- 108 | 109 | def zoomFull(self): 110 | """Zoom to the map full extent.""" 111 | pass 112 | 113 | def zoomToPrevious(self): 114 | """Zoom to previous view extent.""" 115 | pass 116 | 117 | def zoomToNext(self): 118 | """Zoom to next view extent.""" 119 | pass 120 | 121 | def zoomToActiveLayer(self): 122 | """Zoom to extent of active layer.""" 123 | pass 124 | 125 | def addVectorLayer(self, path, base_name, provider_key): 126 | """Add a vector layer. 127 | 128 | :param path: Path to layer. 129 | :type path: str 130 | 131 | :param base_name: Base name for layer. 132 | :type base_name: str 133 | 134 | :param provider_key: Provider key e.g. 'ogr' 135 | :type provider_key: str 136 | """ 137 | pass 138 | 139 | def addRasterLayer(self, path, base_name): 140 | """Add a raster layer given a raster layer file name 141 | 142 | :param path: Path to layer. 143 | :type path: str 144 | 145 | :param base_name: Base name for layer. 146 | :type base_name: str 147 | """ 148 | pass 149 | 150 | def activeLayer(self): 151 | """Get pointer to the active layer (layer selected in the legend).""" 152 | # noinspection PyArgumentList 153 | layers = QgsMapLayerRegistry.instance().mapLayers() 154 | for item in layers: 155 | return layers[item] 156 | 157 | def addToolBarIcon(self, action): 158 | """Add an icon to the plugins toolbar. 159 | 160 | :param action: Action to add to the toolbar. 161 | :type action: QAction 162 | """ 163 | pass 164 | 165 | def removeToolBarIcon(self, action): 166 | """Remove an action (icon) from the plugin toolbar. 167 | 168 | :param action: Action to add to the toolbar. 169 | :type action: QAction 170 | """ 171 | pass 172 | 173 | def addToolBar(self, name): 174 | """Add toolbar with specified name. 175 | 176 | :param name: Name for the toolbar. 177 | :type name: str 178 | """ 179 | pass 180 | 181 | def mapCanvas(self): 182 | """Return a pointer to the map canvas.""" 183 | return self.canvas 184 | 185 | def mainWindow(self): 186 | """Return a pointer to the main window. 187 | 188 | In case of QGIS it returns an instance of QgisApp. 189 | """ 190 | pass 191 | 192 | def addDockWidget(self, area, dock_widget): 193 | """Add a dock widget to the main window. 194 | 195 | :param area: Where in the ui the dock should be placed. 196 | :type area: 197 | 198 | :param dock_widget: A dock widget to add to the UI. 199 | :type dock_widget: QDockWidget 200 | """ 201 | pass 202 | 203 | def legendInterface(self): 204 | """Get the legend.""" 205 | return self.canvas 206 | -------------------------------------------------------------------------------- /processing_provider/rvt_multi_hillshade.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTMultiHillshade(QgsProcessingAlgorithm): 19 | """ 20 | RVT Multiple directions hillshade. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | NUM_DIRECTIONS = 'NUM_DIRECTIONS' 26 | SUN_ELEVATION = 'SUN_ELEVATION' 27 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 28 | OUTPUT = 'OUTPUT' 29 | 30 | def tr(self, string): 31 | """ 32 | Returns a translatable string with the self.tr() function. 33 | """ 34 | return QCoreApplication.translate('Processing', string) 35 | 36 | def createInstance(self): 37 | return RVTMultiHillshade() 38 | 39 | def name(self): 40 | """ 41 | Returns the algorithm name, used for identifying the algorithm. This 42 | string should be fixed for the algorithm, and must not be localised. 43 | The name should be unique within each provider. Names should contain 44 | lowercase alphanumeric characters only and no spaces or other 45 | formatting characters. 46 | """ 47 | return 'rvt_multi_hillshade' 48 | 49 | def displayName(self): 50 | """ 51 | Returns the translated algorithm name, which should be used for any 52 | user-visible display of the algorithm name. 53 | """ 54 | return self.tr('RVT Multiple directions hillshade') 55 | 56 | def shortHelpString(self): 57 | """ 58 | Returns a localised short helper string for the algorithm. This string 59 | should provide a basic description about what the algorithm does and the 60 | parameters and outputs associated with it.. 61 | """ 62 | return self.tr("Relief visualization toolbox, Multiple direction hillshade." 63 | " Calculates hillshade in multiple directions.") 64 | 65 | def initAlgorithm(self, config=None): 66 | """ 67 | Here we define the inputs and output of the algorithm, along 68 | with some other properties. 69 | """ 70 | self.addParameter( 71 | QgsProcessingParameterRasterLayer( 72 | self.INPUT, 73 | self.tr('Input DEM raster layer'), 74 | [QgsProcessing.TypeRaster] 75 | ) 76 | ) 77 | self.addParameter( 78 | QgsProcessingParameterNumber( 79 | name="VE_FACTOR", 80 | description="Vertical exaggeration factor", 81 | type=QgsProcessingParameterNumber.Double, 82 | defaultValue=1, 83 | minValue=-1000, 84 | maxValue=1000 85 | ) 86 | ) 87 | self.addParameter( 88 | QgsProcessingParameterNumber( 89 | name="NUM_DIRECTIONS", 90 | description="Number of solar azimuth angles (bands).", 91 | type=QgsProcessingParameterNumber.Integer, 92 | defaultValue=16, 93 | minValue=2, 94 | maxValue=64 95 | ) 96 | ) 97 | self.addParameter( 98 | QgsProcessingParameterNumber( 99 | name="SUN_ELEVATION", 100 | description="Solar vertical angle (above the horizon) in degrees.", 101 | type=QgsProcessingParameterNumber.Double, 102 | defaultValue=35, 103 | minValue=0, 104 | maxValue=90 105 | ) 106 | ) 107 | self.addParameter( 108 | QgsProcessingParameterBoolean( 109 | name="SAVE_AS_8BIT", 110 | description="Save as 8bit raster", 111 | defaultValue=False 112 | ) 113 | ) 114 | self.addParameter( 115 | QgsProcessingParameterRasterDestination( 116 | self.OUTPUT, 117 | self.tr('Output visualization raster layer') 118 | ) 119 | ) 120 | 121 | def processAlgorithm(self, parameters, context, feedback): 122 | """ 123 | Here is where the processing itself takes place. 124 | """ 125 | dem_layer = self.parameterAsRasterLayer( 126 | parameters, 127 | self.INPUT, 128 | context 129 | ) 130 | 131 | ve_factor = float(self.parameterAsDouble( 132 | parameters, 133 | self.VE_FACTOR, 134 | context 135 | )) 136 | nr_dir = int(self.parameterAsInt( 137 | parameters, 138 | self.NUM_DIRECTIONS, 139 | context 140 | )) 141 | sun_elevation = float(self.parameterAsDouble( 142 | parameters, 143 | self.SUN_ELEVATION, 144 | context 145 | )) 146 | save_8bit = bool(self.parameterAsBool( 147 | parameters, 148 | self.SAVE_AS_8BIT, 149 | context 150 | )) 151 | visualization_path = (self.parameterAsOutputLayer( 152 | parameters, 153 | self.OUTPUT, 154 | context, 155 | )) 156 | 157 | dem_path = str(dem_layer.source()) 158 | 159 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 160 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 161 | dem_arr = dict_arr_dem["array"] 162 | no_data = dict_arr_dem["no_data"] 163 | 164 | visualization_arr = rvt.vis.multi_hillshade(dem=dem_arr, resolution_x=resolution[0], resolution_y=resolution[1], 165 | nr_directions=nr_dir, sun_elevation=sun_elevation, 166 | ve_factor=ve_factor, no_data=no_data) 167 | if not save_8bit: 168 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 169 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 170 | else: 171 | default = rvt.default.DefaultValues() # we have to use this class to calculate 8bita 172 | visualization_8bit_arr = default.float_to_8bit( 173 | float_arr=dem_arr, 174 | visualization=RVTVisualization.MULTI_HILLSHADE, 175 | x_res=resolution[0], 176 | y_res=resolution[1], 177 | no_data=no_data 178 | ) 179 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 180 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 181 | 182 | result = {self.OUTPUT: visualization_path} 183 | return result 184 | -------------------------------------------------------------------------------- /processing_provider/rvt_msrm.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTMsrm(QgsProcessingAlgorithm): 19 | """ 20 | RVT Multi-scale relief model. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | FEATURE_MIN = "FEATURE_MIN" 26 | FEATURE_MAX = "FEATURE_MAX" 27 | SCALING_FACTOR = "SCALING_FACTOR" 28 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 29 | OUTPUT = 'OUTPUT' 30 | 31 | def tr(self, string): 32 | """ 33 | Returns a translatable string with the self.tr() function. 34 | """ 35 | return QCoreApplication.translate('Processing', string) 36 | 37 | def createInstance(self): 38 | return RVTMsrm() 39 | 40 | def name(self): 41 | """ 42 | Returns the algorithm name, used for identifying the algorithm. This 43 | string should be fixed for the algorithm, and must not be localised. 44 | The name should be unique within each provider. Names should contain 45 | lowercase alphanumeric characters only and no spaces or other 46 | formatting characters. 47 | """ 48 | return 'rvt_msrm' 49 | 50 | def displayName(self): 51 | """ 52 | Returns the translated algorithm name, which should be used for any 53 | user-visible display of the algorithm name. 54 | """ 55 | return self.tr('RVT Multi-scale relief model') 56 | 57 | def shortHelpString(self): 58 | """ 59 | Returns a localised short helper string for the algorithm. This string 60 | should provide a basic description about what the algorithm does and the 61 | parameters and outputs associated with it.. 62 | """ 63 | return self.tr("Relief visualization toolbox, Multi-scale relief model. Calculates Multi-scale relief model.") 64 | 65 | def initAlgorithm(self, config=None): 66 | """ 67 | Here we define the inputs and output of the algorithm, along 68 | with some other properties. 69 | """ 70 | self.addParameter( 71 | QgsProcessingParameterRasterLayer( 72 | self.INPUT, 73 | self.tr('Input DEM raster layer'), 74 | [QgsProcessing.TypeRaster] 75 | ) 76 | ) 77 | self.addParameter( 78 | QgsProcessingParameterNumber( 79 | name="VE_FACTOR", 80 | description="Vertical exaggeration factor", 81 | type=QgsProcessingParameterNumber.Double, 82 | defaultValue=1, 83 | minValue=-1000, 84 | maxValue=1000 85 | ) 86 | ) 87 | self.addParameter( 88 | QgsProcessingParameterNumber( 89 | name="FEATURE_MIN", 90 | description="Feature minimum", 91 | type=QgsProcessingParameterNumber.Double, 92 | defaultValue=1, 93 | minValue=0, 94 | maxValue=1000 95 | ) 96 | ) 97 | self.addParameter( 98 | QgsProcessingParameterNumber( 99 | name="FEATURE_MAX", 100 | description="Feature maximum", 101 | type=QgsProcessingParameterNumber.Double, 102 | defaultValue=5, 103 | minValue=0, 104 | maxValue=1000 105 | ) 106 | ) 107 | self.addParameter( 108 | QgsProcessingParameterNumber( 109 | name="SCALING_FACTOR", 110 | description="Scaling factor", 111 | type=QgsProcessingParameterNumber.Integer, 112 | defaultValue=3, 113 | minValue=1, 114 | maxValue=20 115 | ) 116 | ) 117 | self.addParameter( 118 | QgsProcessingParameterBoolean( 119 | name="SAVE_AS_8BIT", 120 | description="Save as 8bit raster", 121 | defaultValue=False 122 | ) 123 | ) 124 | self.addParameter( 125 | QgsProcessingParameterRasterDestination( 126 | self.OUTPUT, 127 | self.tr('Output visualization raster layer') 128 | ) 129 | ) 130 | 131 | def processAlgorithm(self, parameters, context, feedback): 132 | """ 133 | Here is where the processing itself takes place. 134 | """ 135 | dem_layer = self.parameterAsRasterLayer( 136 | parameters, 137 | self.INPUT, 138 | context 139 | ) 140 | ve_factor = float(self.parameterAsDouble( 141 | parameters, 142 | self.VE_FACTOR, 143 | context 144 | )) 145 | feature_min = float(self.parameterAsDouble( 146 | parameters, 147 | self.FEATURE_MIN, 148 | context 149 | )) 150 | feature_max = float(self.parameterAsDouble( 151 | parameters, 152 | self.FEATURE_MAX, 153 | context 154 | )) 155 | scaling_factor = int(self.parameterAsDouble( 156 | parameters, 157 | self.SCALING_FACTOR, 158 | context 159 | )) 160 | save_8bit = bool(self.parameterAsBool( 161 | parameters, 162 | self.SAVE_AS_8BIT, 163 | context 164 | )) 165 | visualization_path = (self.parameterAsOutputLayer( 166 | parameters, 167 | self.OUTPUT, 168 | context, 169 | )) 170 | 171 | dem_path = str(dem_layer.source()) 172 | 173 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 174 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 175 | dem_arr = dict_arr_dem["array"] 176 | no_data = dict_arr_dem["no_data"] 177 | 178 | visualization_arr = rvt.vis.msrm(dem=dem_arr, resolution=resolution[0], feature_min=feature_min, 179 | feature_max=feature_max, scaling_factor=scaling_factor, 180 | ve_factor=ve_factor, no_data=no_data) 181 | if not save_8bit: 182 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 183 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 184 | else: 185 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 186 | float_arr=visualization_arr, visualization=RVTVisualization.MULTI_SCALE_RELIEF_MODEL) 187 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 188 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 189 | 190 | result = {self.OUTPUT: visualization_path} 191 | return result 192 | -------------------------------------------------------------------------------- /processing_provider/rvt_svf.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTSvf(QgsProcessingAlgorithm): 19 | """ 20 | RVT Sky-view factor. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | RADIUS = "RADIUS" 26 | NUM_DIRECTIONS = "NUM_DIRECTIONS" 27 | NOISE_REMOVE = "NOISE_REMOVE" 28 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 29 | OUTPUT = 'OUTPUT' 30 | 31 | noise_options = ["no removal", "low", "medium", "high"] 32 | 33 | def tr(self, string): 34 | """ 35 | Returns a translatable string with the self.tr() function. 36 | """ 37 | return QCoreApplication.translate('Processing', string) 38 | 39 | def createInstance(self): 40 | return RVTSvf() 41 | 42 | def name(self): 43 | """ 44 | Returns the algorithm name, used for identifying the algorithm. This 45 | string should be fixed for the algorithm, and must not be localised. 46 | The name should be unique within each provider. Names should contain 47 | lowercase alphanumeric characters only and no spaces or other 48 | formatting characters. 49 | """ 50 | return 'rvt_svf' 51 | 52 | def displayName(self): 53 | """ 54 | Returns the translated algorithm name, which should be used for any 55 | user-visible display of the algorithm name. 56 | """ 57 | return self.tr('RVT Sky-view factor') 58 | 59 | def shortHelpString(self): 60 | """ 61 | Returns a localised short helper string for the algorithm. This string 62 | should provide a basic description about what the algorithm does and the 63 | parameters and outputs associated with it.. 64 | """ 65 | return self.tr("Relief visualization toolbox, Sky-view factor. Calculates Sky-view factor.") 66 | 67 | def initAlgorithm(self, config=None): 68 | """ 69 | Here we define the inputs and output of the algorithm, along 70 | with some other properties. 71 | """ 72 | self.addParameter( 73 | QgsProcessingParameterRasterLayer( 74 | self.INPUT, 75 | self.tr('Input DEM raster layer'), 76 | [QgsProcessing.TypeRaster] 77 | ) 78 | ) 79 | self.addParameter( 80 | QgsProcessingParameterNumber( 81 | name="VE_FACTOR", 82 | description="Vertical exaggeration factor", 83 | type=QgsProcessingParameterNumber.Double, 84 | defaultValue=1, 85 | minValue=-1000, 86 | maxValue=1000 87 | ) 88 | ) 89 | self.addParameter( 90 | QgsProcessingParameterNumber( 91 | name="RADIUS", 92 | description="Search radius [pixels]", 93 | type=QgsProcessingParameterNumber.Integer, 94 | defaultValue=10, 95 | minValue=10, 96 | maxValue=50 97 | ) 98 | ) 99 | self.addParameter( 100 | QgsProcessingParameterNumber( 101 | name="NUM_DIRECTIONS", 102 | description="Number of search directions", 103 | type=QgsProcessingParameterNumber.Integer, 104 | defaultValue=16, 105 | minValue=8, 106 | maxValue=64 107 | ) 108 | ) 109 | self.addParameter( 110 | QgsProcessingParameterEnum( 111 | name="NOISE_REMOVE", 112 | description="Level of noise removal", 113 | options=self.noise_options, 114 | defaultValue="no removal" 115 | ) 116 | ) 117 | self.addParameter( 118 | QgsProcessingParameterBoolean( 119 | name="SAVE_AS_8BIT", 120 | description="Save as 8bit raster", 121 | defaultValue=False 122 | ) 123 | ) 124 | self.addParameter( 125 | QgsProcessingParameterRasterDestination( 126 | self.OUTPUT, 127 | self.tr('Output visualization raster layer') 128 | ) 129 | ) 130 | 131 | def processAlgorithm(self, parameters, context, feedback): 132 | """ 133 | Here is where the processing itself takes place. 134 | """ 135 | dem_layer = self.parameterAsRasterLayer( 136 | parameters, 137 | self.INPUT, 138 | context 139 | ) 140 | 141 | ve_factor = float(self.parameterAsDouble( 142 | parameters, 143 | self.VE_FACTOR, 144 | context 145 | )) 146 | radius = int(self.parameterAsInt( 147 | parameters, 148 | self.RADIUS, 149 | context 150 | )) 151 | nr_dir = int(self.parameterAsInt( 152 | parameters, 153 | self.NUM_DIRECTIONS, 154 | context 155 | )) 156 | noise = int(self.parameterAsEnum( 157 | parameters, 158 | self.NOISE_REMOVE, 159 | context 160 | )) 161 | save_8bit = bool(self.parameterAsBool( 162 | parameters, 163 | self.SAVE_AS_8BIT, 164 | context 165 | )) 166 | visualization_path = (self.parameterAsOutputLayer( 167 | parameters, 168 | self.OUTPUT, 169 | context, 170 | )) 171 | 172 | dem_path = str(dem_layer.source()) 173 | 174 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 175 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 176 | dem_arr = dict_arr_dem["array"] 177 | no_data = dict_arr_dem["no_data"] 178 | 179 | visualization_arr = rvt.vis.sky_view_factor(dem=dem_arr, resolution=resolution[0], compute_svf=True, 180 | compute_asvf=False, compute_opns=False, svf_n_dir=nr_dir, 181 | svf_r_max=radius, svf_noise=noise, ve_factor=ve_factor, 182 | no_data=no_data)["svf"] 183 | if not save_8bit: 184 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 185 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 186 | else: 187 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 188 | float_arr=visualization_arr, visualization=RVTVisualization.SKY_VIEW_FACTOR 189 | ) 190 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 191 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 192 | result = {self.OUTPUT: visualization_path} 193 | return result 194 | -------------------------------------------------------------------------------- /processing_provider/rvt_local_dom.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTLocalDom(QgsProcessingAlgorithm): 19 | """ 20 | RVT Local dominance. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | MIN_RADIUS = "MIN_RADIUS" 26 | MAX_RADIUS = "MAX_RADIUS" 27 | ANGULAR_RES = "ANGULAR_RES" 28 | OBSERVER_H = "OBSERVER_H" 29 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 30 | OUTPUT = 'OUTPUT' 31 | 32 | def tr(self, string): 33 | """ 34 | Returns a translatable string with the self.tr() function. 35 | """ 36 | return QCoreApplication.translate('Processing', string) 37 | 38 | def createInstance(self): 39 | return RVTLocalDom() 40 | 41 | def name(self): 42 | """ 43 | Returns the algorithm name, used for identifying the algorithm. This 44 | string should be fixed for the algorithm, and must not be localised. 45 | The name should be unique within each provider. Names should contain 46 | lowercase alphanumeric characters only and no spaces or other 47 | formatting characters. 48 | """ 49 | return 'rvt_ld' 50 | 51 | def displayName(self): 52 | """ 53 | Returns the translated algorithm name, which should be used for any 54 | user-visible display of the algorithm name. 55 | """ 56 | return self.tr('RVT Local dominance') 57 | 58 | def shortHelpString(self): 59 | """ 60 | Returns a localised short helper string for the algorithm. This string 61 | should provide a basic description about what the algorithm does and the 62 | parameters and outputs associated with it.. 63 | """ 64 | return self.tr("Relief visualization toolbox, Local dominance. Calculates Local dominance.") 65 | 66 | def initAlgorithm(self, config=None): 67 | """ 68 | Here we define the inputs and output of the algorithm, along 69 | with some other properties. 70 | """ 71 | self.addParameter( 72 | QgsProcessingParameterRasterLayer( 73 | self.INPUT, 74 | self.tr('Input DEM raster layer'), 75 | [QgsProcessing.TypeRaster] 76 | ) 77 | ) 78 | self.addParameter( 79 | QgsProcessingParameterNumber( 80 | name="VE_FACTOR", 81 | description="Vertical exaggeration factor", 82 | type=QgsProcessingParameterNumber.Double, 83 | defaultValue=1, 84 | minValue=-1000, 85 | maxValue=1000 86 | ) 87 | ) 88 | self.addParameter( 89 | QgsProcessingParameterNumber( 90 | name="MIN_RADIUS", 91 | description="Minimum radius [pixels]", 92 | type=QgsProcessingParameterNumber.Integer, 93 | defaultValue=10, 94 | minValue=0, 95 | maxValue=100 96 | ) 97 | ) 98 | self.addParameter( 99 | QgsProcessingParameterNumber( 100 | name="MAX_RADIUS", 101 | description="Maximum radius [pixels]", 102 | type=QgsProcessingParameterNumber.Integer, 103 | defaultValue=20, 104 | minValue=0, 105 | maxValue=100 106 | ) 107 | ) 108 | self.addParameter( 109 | QgsProcessingParameterNumber( 110 | name="ANGULAR_RES", 111 | description="Number of angular directions", 112 | type=QgsProcessingParameterNumber.Integer, 113 | defaultValue=15, 114 | minValue=4, 115 | maxValue=32 116 | ) 117 | ) 118 | self.addParameter( 119 | QgsProcessingParameterNumber( 120 | name="OBSERVER_H", 121 | description="Height at which we observe the terrain", 122 | type=QgsProcessingParameterNumber.Double, 123 | defaultValue=1.7, 124 | minValue=0.5, 125 | maxValue=20 126 | ) 127 | ) 128 | self.addParameter( 129 | QgsProcessingParameterBoolean( 130 | name="SAVE_AS_8BIT", 131 | description="Save as 8bit raster", 132 | defaultValue=False 133 | ) 134 | ) 135 | self.addParameter( 136 | QgsProcessingParameterRasterDestination( 137 | self.OUTPUT, 138 | self.tr('Output visualization raster layer') 139 | ) 140 | ) 141 | 142 | def processAlgorithm(self, parameters, context, feedback): 143 | """ 144 | Here is where the processing itself takes place. 145 | """ 146 | dem_layer = self.parameterAsRasterLayer( 147 | parameters, 148 | self.INPUT, 149 | context 150 | ) 151 | 152 | ve_factor = float(self.parameterAsDouble( 153 | parameters, 154 | self.VE_FACTOR, 155 | context 156 | )) 157 | min_rad = int(self.parameterAsInt( 158 | parameters, 159 | self.MIN_RADIUS, 160 | context 161 | )) 162 | max_rad = int(self.parameterAsInt( 163 | parameters, 164 | self.MAX_RADIUS, 165 | context 166 | )) 167 | angular_res = int(self.parameterAsInt( 168 | parameters, 169 | self.ANGULAR_RES, 170 | context 171 | )) 172 | observer_h = float(self.parameterAsDouble( 173 | parameters, 174 | self.OBSERVER_H, 175 | context 176 | )) 177 | save_8bit = bool(self.parameterAsBool( 178 | parameters, 179 | self.SAVE_AS_8BIT, 180 | context 181 | )) 182 | visualization_path = (self.parameterAsOutputLayer( 183 | parameters, 184 | self.OUTPUT, 185 | context, 186 | )) 187 | 188 | dem_path = str(dem_layer.source()) 189 | 190 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 191 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 192 | dem_arr = dict_arr_dem["array"] 193 | no_data = dict_arr_dem["no_data"] 194 | 195 | visualization_arr = rvt.vis.local_dominance(dem=dem_arr, min_rad=min_rad, max_rad=max_rad, 196 | angular_res=angular_res, observer_height=observer_h, 197 | ve_factor=ve_factor, no_data=no_data) 198 | if not save_8bit: 199 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 200 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 201 | else: 202 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 203 | float_arr=visualization_arr, visualization=RVTVisualization.LOCAL_DOMINANCE 204 | ) 205 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 206 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 207 | 208 | result = {self.OUTPUT: visualization_path} 209 | return result 210 | -------------------------------------------------------------------------------- /processing_provider/rvt_opns.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTOpns(QgsProcessingAlgorithm): 19 | """ 20 | RVT Sky-view factor. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | RADIUS = "RADIUS" 26 | NUM_DIRECTIONS = "NUM_DIRECTIONS" 27 | NOISE_REMOVE = "NOISE_REMOVE" 28 | OPNS_TYPE = "OPNS_TYPE" 29 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 30 | OUTPUT = 'OUTPUT' 31 | 32 | noise_options = ["no removal", "low", "medium", "high"] 33 | opns_options = ["Positive", "Negative"] 34 | 35 | def tr(self, string): 36 | """ 37 | Returns a translatable string with the self.tr() function. 38 | """ 39 | return QCoreApplication.translate('Processing', string) 40 | 41 | def createInstance(self): 42 | return RVTOpns() 43 | 44 | def name(self): 45 | """ 46 | Returns the algorithm name, used for identifying the algorithm. This 47 | string should be fixed for the algorithm, and must not be localised. 48 | The name should be unique within each provider. Names should contain 49 | lowercase alphanumeric characters only and no spaces or other 50 | formatting characters. 51 | """ 52 | return 'rvt_opns' 53 | 54 | def displayName(self): 55 | """ 56 | Returns the translated algorithm name, which should be used for any 57 | user-visible display of the algorithm name. 58 | """ 59 | return self.tr('RVT Openness') 60 | 61 | def shortHelpString(self): 62 | """ 63 | Returns a localised short helper string for the algorithm. This string 64 | should provide a basic description about what the algorithm does and the 65 | parameters and outputs associated with it.. 66 | """ 67 | return self.tr("Relief visualization toolbox, Openness. Calculates Positive or Negative Openness.") 68 | 69 | def initAlgorithm(self, config=None): 70 | """ 71 | Here we define the inputs and output of the algorithm, along 72 | with some other properties. 73 | """ 74 | self.addParameter( 75 | QgsProcessingParameterRasterLayer( 76 | self.INPUT, 77 | self.tr('Input DEM raster layer'), 78 | [QgsProcessing.TypeRaster] 79 | ) 80 | ) 81 | self.addParameter( 82 | QgsProcessingParameterNumber( 83 | name="VE_FACTOR", 84 | description="Vertical exaggeration factor", 85 | type=QgsProcessingParameterNumber.Double, 86 | defaultValue=1, 87 | minValue=-1000, 88 | maxValue=1000 89 | ) 90 | ) 91 | self.addParameter( 92 | QgsProcessingParameterNumber( 93 | name="RADIUS", 94 | description="Search radius [pixels]", 95 | type=QgsProcessingParameterNumber.Integer, 96 | defaultValue=10, 97 | minValue=10, 98 | maxValue=50 99 | ) 100 | ) 101 | self.addParameter( 102 | QgsProcessingParameterNumber( 103 | name="NUM_DIRECTIONS", 104 | description="Number of search directions", 105 | type=QgsProcessingParameterNumber.Integer, 106 | defaultValue=16, 107 | minValue=8, 108 | maxValue=64 109 | ) 110 | ) 111 | self.addParameter( 112 | QgsProcessingParameterEnum( 113 | name="NOISE_REMOVE", 114 | description="Level of noise removal", 115 | options=self.noise_options, 116 | defaultValue="no removal" 117 | ) 118 | ) 119 | self.addParameter( 120 | QgsProcessingParameterEnum( 121 | name="OPNS_TYPE", 122 | description="Which Openness: Positive or Negative", 123 | options=self.opns_options, 124 | defaultValue="Positive" 125 | ) 126 | ) 127 | self.addParameter( 128 | QgsProcessingParameterBoolean( 129 | name="SAVE_AS_8BIT", 130 | description="Save as 8bit raster", 131 | defaultValue=False 132 | ) 133 | ) 134 | self.addParameter( 135 | QgsProcessingParameterRasterDestination( 136 | self.OUTPUT, 137 | self.tr('Output visualization raster layer') 138 | ) 139 | ) 140 | 141 | def processAlgorithm(self, parameters, context, feedback): 142 | """ 143 | Here is where the processing itself takes place. 144 | """ 145 | dem_layer = self.parameterAsRasterLayer( 146 | parameters, 147 | self.INPUT, 148 | context 149 | ) 150 | 151 | ve_factor = float(self.parameterAsDouble( 152 | parameters, 153 | self.VE_FACTOR, 154 | context 155 | )) 156 | radius = int(self.parameterAsInt( 157 | parameters, 158 | self.RADIUS, 159 | context 160 | )) 161 | nr_dir = int(self.parameterAsInt( 162 | parameters, 163 | self.NUM_DIRECTIONS, 164 | context 165 | )) 166 | noise = int(self.parameterAsEnum( 167 | parameters, 168 | self.NOISE_REMOVE, 169 | context 170 | )) 171 | opns_type = int(self.parameterAsEnum( 172 | parameters, 173 | self.OPNS_TYPE, 174 | context 175 | )) 176 | save_8bit = bool(self.parameterAsBool( 177 | parameters, 178 | self.SAVE_AS_8BIT, 179 | context 180 | )) 181 | visualization_path = (self.parameterAsOutputLayer( 182 | parameters, 183 | self.OUTPUT, 184 | context, 185 | )) 186 | 187 | dem_path = str(dem_layer.source()) 188 | 189 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 190 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 191 | dem_arr = dict_arr_dem["array"] 192 | no_data = dict_arr_dem["no_data"] 193 | 194 | vis = RVTVisualization.POSITIVE_OPENNESS 195 | if opns_type == 1: 196 | vis = RVTVisualization.NEGATIVE_OPENNESS 197 | dem_arr = dem_arr * -1 # negative openness is openness where dem * -1 198 | visualization_arr = rvt.vis.sky_view_factor(dem=dem_arr, resolution=resolution[0], compute_svf=False, 199 | compute_asvf=False, compute_opns=True, svf_n_dir=nr_dir, 200 | svf_r_max=radius, svf_noise=noise, ve_factor=ve_factor, 201 | no_data=no_data)["opns"] 202 | if not save_8bit: 203 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 204 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 205 | else: 206 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit(float_arr=visualization_arr, 207 | visualization=vis) 208 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 209 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 210 | 211 | result = {self.OUTPUT: visualization_path} 212 | return result 213 | -------------------------------------------------------------------------------- /processing_provider/rvt_asvf.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTASvf(QgsProcessingAlgorithm): 19 | """ 20 | RVT Anisotropic Sky-view factor. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | RADIUS = "RADIUS" 26 | NUM_DIRECTIONS = "NUM_DIRECTIONS" 27 | NOISE_REMOVE = "NOISE_REMOVE" 28 | ANISOTROPY_LVL = "ANISOTROPY_LVL" 29 | ANISOTROPY_DIR = "ANISOTROPY_DIR" 30 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 31 | OUTPUT = 'OUTPUT' 32 | 33 | noise_options = ["no removal", "low", "medium", "high"] 34 | ani_lvl_options = ["low", "high"] 35 | 36 | def tr(self, string): 37 | """ 38 | Returns a translatable string with the self.tr() function. 39 | """ 40 | return QCoreApplication.translate('Processing', string) 41 | 42 | def createInstance(self): 43 | return RVTASvf() 44 | 45 | def name(self): 46 | """ 47 | Returns the algorithm name, used for identifying the algorithm. This 48 | string should be fixed for the algorithm, and must not be localised. 49 | The name should be unique within each provider. Names should contain 50 | lowercase alphanumeric characters only and no spaces or other 51 | formatting characters. 52 | """ 53 | return 'rvt_asvf' 54 | 55 | def displayName(self): 56 | """ 57 | Returns the translated algorithm name, which should be used for any 58 | user-visible display of the algorithm name. 59 | """ 60 | return self.tr('RVT Anisotropic Sky-view factor') 61 | 62 | def shortHelpString(self): 63 | """ 64 | Returns a localised short helper string for the algorithm. This string 65 | should provide a basic description about what the algorithm does and the 66 | parameters and outputs associated with it.. 67 | """ 68 | return self.tr("Relief visualization toolbox, Anisotropic Sky-view factor. Calculates " 69 | "Anisotropic Sky-view factor.") 70 | 71 | def initAlgorithm(self, config=None): 72 | """ 73 | Here we define the inputs and output of the algorithm, along 74 | with some other properties. 75 | """ 76 | self.addParameter( 77 | QgsProcessingParameterRasterLayer( 78 | self.INPUT, 79 | self.tr('Input DEM raster layer'), 80 | [QgsProcessing.TypeRaster] 81 | ) 82 | ) 83 | self.addParameter( 84 | QgsProcessingParameterNumber( 85 | name="VE_FACTOR", 86 | description="Vertical exaggeration factor", 87 | type=QgsProcessingParameterNumber.Double, 88 | defaultValue=1, 89 | minValue=-1000, 90 | maxValue=1000 91 | ) 92 | ) 93 | self.addParameter( 94 | QgsProcessingParameterNumber( 95 | name="RADIUS", 96 | description="Search radius [pixels]", 97 | type=QgsProcessingParameterNumber.Integer, 98 | defaultValue=10, 99 | minValue=10, 100 | maxValue=50 101 | ) 102 | ) 103 | self.addParameter( 104 | QgsProcessingParameterNumber( 105 | name="NUM_DIRECTIONS", 106 | description="Number of search directions", 107 | type=QgsProcessingParameterNumber.Integer, 108 | defaultValue=16, 109 | minValue=8, 110 | maxValue=64 111 | ) 112 | ) 113 | self.addParameter( 114 | QgsProcessingParameterEnum( 115 | name="NOISE_REMOVE", 116 | description="Level of noise removal", 117 | options=self.noise_options, 118 | defaultValue="no removal" 119 | ) 120 | ) 121 | self.addParameter( 122 | QgsProcessingParameterEnum( 123 | name="ANISOTROPY_LVL", 124 | description="Level of anisotropy", 125 | options=self.ani_lvl_options, 126 | defaultValue="low" 127 | ) 128 | ) 129 | self.addParameter( 130 | QgsProcessingParameterNumber( 131 | name="ANISOTROPY_DIR", 132 | description="Main direction of anisotropy [deg]", 133 | type=QgsProcessingParameterNumber.Double, 134 | defaultValue=315, 135 | minValue=0, 136 | maxValue=360 137 | ) 138 | ) 139 | self.addParameter( 140 | QgsProcessingParameterBoolean( 141 | name="SAVE_AS_8BIT", 142 | description="Save as 8bit raster", 143 | defaultValue=False 144 | ) 145 | ) 146 | self.addParameter( 147 | QgsProcessingParameterRasterDestination( 148 | self.OUTPUT, 149 | self.tr('Output visualization raster layer') 150 | ) 151 | ) 152 | 153 | def processAlgorithm(self, parameters, context, feedback): 154 | """ 155 | Here is where the processing itself takes place. 156 | """ 157 | dem_layer = self.parameterAsRasterLayer( 158 | parameters, 159 | self.INPUT, 160 | context 161 | ) 162 | 163 | ve_factor = float(self.parameterAsDouble( 164 | parameters, 165 | self.VE_FACTOR, 166 | context 167 | )) 168 | radius = int(self.parameterAsInt( 169 | parameters, 170 | self.RADIUS, 171 | context 172 | )) 173 | nr_dir = int(self.parameterAsInt( 174 | parameters, 175 | self.NUM_DIRECTIONS, 176 | context 177 | )) 178 | noise = int(self.parameterAsEnum( 179 | parameters, 180 | self.NOISE_REMOVE, 181 | context 182 | )) 183 | asvf_lvl = int(self.parameterAsEnum( 184 | parameters, 185 | self.ANISOTROPY_LVL, 186 | context 187 | )) 188 | asvf_lvl += 1 189 | asvf_dir = float(self.parameterAsDouble( 190 | parameters, 191 | self.ANISOTROPY_DIR, 192 | context 193 | )) 194 | save_8bit = bool(self.parameterAsBool( 195 | parameters, 196 | self.SAVE_AS_8BIT, 197 | context 198 | )) 199 | visualization_path = (self.parameterAsOutputLayer( 200 | parameters, 201 | self.OUTPUT, 202 | context, 203 | )) 204 | 205 | dem_path = str(dem_layer.source()) 206 | 207 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 208 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 209 | dem_arr = dict_arr_dem["array"] 210 | no_data = dict_arr_dem["no_data"] 211 | 212 | visualization_arr = rvt.vis.sky_view_factor(dem=dem_arr, resolution=resolution[0], compute_svf=False, 213 | compute_asvf=True, compute_opns=False, svf_n_dir=nr_dir, 214 | svf_r_max=radius, svf_noise=noise, ve_factor=ve_factor, 215 | asvf_level=asvf_lvl, asvf_dir=asvf_dir, no_data=no_data)["asvf"] 216 | if not save_8bit: 217 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 218 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 219 | else: 220 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 221 | float_arr=visualization_arr, visualization=RVTVisualization.ANISOTROPIC_SKY_VIEW_FACTOR 222 | ) 223 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 224 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 225 | 226 | result = {self.OUTPUT: visualization_path} 227 | return result 228 | -------------------------------------------------------------------------------- /processing_provider/rvt_sky_illum.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTSim(QgsProcessingAlgorithm): 19 | """ 20 | RVT Sky illumination. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | SKY_MODEL = "SKY_MODEL" 26 | NUM_DIRECTIONS = "NUM_DIRECTIONS" 27 | SHADOW_DIST = "SHADOW_DIST" 28 | SHADOW_AZIMUTH = "SHADOW_AZIMUTH" 29 | SHADOW_ELEVATION = "SHADOW_ELEVATION" 30 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 31 | OUTPUT = 'OUTPUT' 32 | 33 | sky_model_options = ["overcast", "uniform"] 34 | 35 | def tr(self, string): 36 | """ 37 | Returns a translatable string with the self.tr() function. 38 | """ 39 | return QCoreApplication.translate('Processing', string) 40 | 41 | def createInstance(self): 42 | return RVTSim() 43 | 44 | def name(self): 45 | """ 46 | Returns the algorithm name, used for identifying the algorithm. This 47 | string should be fixed for the algorithm, and must not be localised. 48 | The name should be unique within each provider. Names should contain 49 | lowercase alphanumeric characters only and no spaces or other 50 | formatting characters. 51 | """ 52 | return 'rvt_sim' 53 | 54 | def displayName(self): 55 | """ 56 | Returns the translated algorithm name, which should be used for any 57 | user-visible display of the algorithm name. 58 | """ 59 | return self.tr('RVT Sky illumination') 60 | 61 | def shortHelpString(self): 62 | """ 63 | Returns a localised short helper string for the algorithm. This string 64 | should provide a basic description about what the algorithm does and the 65 | parameters and outputs associated with it.. 66 | """ 67 | return self.tr("Relief visualization toolbox, Sky illumination. Calculates Sky illumination.") 68 | 69 | def initAlgorithm(self, config=None): 70 | """ 71 | Here we define the inputs and output of the algorithm, along 72 | with some other properties. 73 | """ 74 | self.addParameter( 75 | QgsProcessingParameterRasterLayer( 76 | self.INPUT, 77 | self.tr('Input DEM raster layer'), 78 | [QgsProcessing.TypeRaster] 79 | ) 80 | ) 81 | self.addParameter( 82 | QgsProcessingParameterNumber( 83 | name="VE_FACTOR", 84 | description="Vertical exaggeration factor", 85 | type=QgsProcessingParameterNumber.Double, 86 | defaultValue=1, 87 | minValue=-1000, 88 | maxValue=1000 89 | ) 90 | ) 91 | self.addParameter( 92 | QgsProcessingParameterEnum( 93 | name="SKY_MODEL", 94 | description="Sky model", 95 | options=self.sky_model_options, 96 | defaultValue="overcast" 97 | ) 98 | ) 99 | self.addParameter( 100 | QgsProcessingParameterNumber( 101 | name="NUM_DIRECTIONS", 102 | description="Number of horizon search directions", 103 | type=QgsProcessingParameterNumber.Integer, 104 | defaultValue=32, 105 | minValue=8, 106 | maxValue=128 107 | ) 108 | ) 109 | self.addParameter( 110 | QgsProcessingParameterNumber( 111 | name="SHADOW_DIST", 112 | description="Max shadow modeling distance", 113 | type=QgsProcessingParameterNumber.Integer, 114 | defaultValue=100, 115 | minValue=10, 116 | maxValue=1000 117 | ) 118 | ) 119 | self.addParameter( 120 | QgsProcessingParameterNumber( 121 | name="SHADOW_AZIMUTH", 122 | description="Shadow azimuth", 123 | type=QgsProcessingParameterNumber.Double, 124 | defaultValue=315, 125 | minValue=0, 126 | maxValue=360 127 | ) 128 | ) 129 | self.addParameter( 130 | QgsProcessingParameterNumber( 131 | name="SHADOW_ELEVATION", 132 | description="Shadow elevation", 133 | type=QgsProcessingParameterNumber.Double, 134 | defaultValue=35, 135 | minValue=0, 136 | maxValue=90 137 | ) 138 | ) 139 | self.addParameter( 140 | QgsProcessingParameterBoolean( 141 | name="SAVE_AS_8BIT", 142 | description="Save as 8bit raster", 143 | defaultValue=False 144 | ) 145 | ) 146 | self.addParameter( 147 | QgsProcessingParameterRasterDestination( 148 | self.OUTPUT, 149 | self.tr('Output visualization raster layer') 150 | ) 151 | ) 152 | 153 | def processAlgorithm(self, parameters, context, feedback): 154 | """ 155 | Here is where the processing itself takes place. 156 | """ 157 | dem_layer = self.parameterAsRasterLayer( 158 | parameters, 159 | self.INPUT, 160 | context 161 | ) 162 | 163 | ve_factor = float(self.parameterAsDouble( 164 | parameters, 165 | self.VE_FACTOR, 166 | context 167 | )) 168 | sky_model_enum = int(self.parameterAsEnum( 169 | parameters, 170 | self.SKY_MODEL, 171 | context 172 | )) 173 | sky_model = self.sky_model_options[sky_model_enum] 174 | nr_dir = int(self.parameterAsInt( 175 | parameters, 176 | self.NUM_DIRECTIONS, 177 | context 178 | )) 179 | max_fine_rad = float(self.parameterAsInt( 180 | parameters, 181 | self.SHADOW_DIST, 182 | context 183 | )) 184 | shadow_az = float(self.parameterAsDouble( 185 | parameters, 186 | self.SHADOW_AZIMUTH, 187 | context 188 | )) 189 | shadow_el = float(self.parameterAsDouble( 190 | parameters, 191 | self.SHADOW_ELEVATION, 192 | context 193 | )) 194 | save_8bit = bool(self.parameterAsBool( 195 | parameters, 196 | self.SAVE_AS_8BIT, 197 | context 198 | )) 199 | visualization_path = (self.parameterAsOutputLayer( 200 | parameters, 201 | self.OUTPUT, 202 | context, 203 | )) 204 | 205 | dem_path = str(dem_layer.source()) 206 | 207 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 208 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 209 | dem_arr = dict_arr_dem["array"] 210 | no_data = dict_arr_dem["no_data"] 211 | 212 | visualization_arr = rvt.vis.sky_illumination(dem=dem_arr, resolution=resolution[0], sky_model=sky_model, 213 | max_fine_radius=max_fine_rad, num_directions=nr_dir, 214 | ve_factor=ve_factor, 215 | compute_shadow=True, shadow_az=shadow_az, shadow_el=shadow_el, 216 | no_data=no_data) 217 | if not save_8bit: 218 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 219 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 220 | else: 221 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 222 | float_arr=visualization_arr, visualization=RVTVisualization.SKY_ILLUMINATION 223 | ) 224 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 225 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 226 | 227 | result = {self.OUTPUT: visualization_path} 228 | return result 229 | -------------------------------------------------------------------------------- /processing_provider/rvt_fill_no_data.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import ( 3 | QgsProcessing, 4 | QgsProcessingAlgorithm, 5 | QgsProcessingParameterRasterLayer, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterDestination, 8 | QgsProcessingParameterEnum 9 | ) 10 | from qgis import processing 11 | import numpy as np 12 | import rvt.default 13 | import rvt.blend 14 | import rvt.vis 15 | import os 16 | 17 | 18 | class RVTFillNoData(QgsProcessingAlgorithm): 19 | """ 20 | RVT Fill no data. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | METHOD = 'METHOD' 25 | OUTPUT = 'OUTPUT' 26 | 27 | method_options = ["kd_tree", "nearest_neighbour"] 28 | 29 | def tr(self, string): 30 | """ 31 | Returns a translatable string with the self.tr() function. 32 | """ 33 | return QCoreApplication.translate('Processing', string) 34 | 35 | def createInstance(self): 36 | return RVTFillNoData() 37 | 38 | def name(self): 39 | """ 40 | Returns the algorithm name, used for identifying the algorithm. This 41 | string should be fixed for the algorithm, and must not be localised. 42 | The name should be unique within each provider. Names should contain 43 | lowercase alphanumeric characters only and no spaces or other 44 | formatting characters. 45 | """ 46 | return 'rvt_fill_no_data' 47 | 48 | def displayName(self): 49 | """ 50 | Returns the translated algorithm name, which should be used for any 51 | user-visible display of the algorithm name. 52 | """ 53 | return self.tr('RVT Fill no-data') 54 | 55 | def shortHelpString(self): 56 | """ 57 | Returns a localised short helper string for the algorithm. This string 58 | should provide a basic description about what the algorithm does and the 59 | parameters and outputs associated with it.. 60 | """ 61 | return self.tr("Relief visualization toolbox, Function to fill no data. Note: DEM has to have defined no-data!") 62 | 63 | def initAlgorithm(self, config=None): 64 | """ 65 | Here we define the inputs and output of the algorithm, along 66 | with some other properties. 67 | """ 68 | self.addParameter( 69 | QgsProcessingParameterRasterLayer( 70 | self.INPUT, 71 | self.tr('Input DEM raster layer'), 72 | [QgsProcessing.TypeRaster] 73 | ) 74 | ) 75 | self.addParameter( 76 | QgsProcessingParameterEnum( 77 | name="METHOD", 78 | description="Interpolation method", 79 | options=self.method_options, 80 | defaultValue="nearest_neighbour" 81 | ) 82 | ) 83 | self.addParameter( 84 | QgsProcessingParameterRasterDestination( 85 | self.OUTPUT, 86 | self.tr('Output visualization raster layer') 87 | ) 88 | ) 89 | 90 | def processAlgorithm(self, parameters, context, feedback): 91 | """ 92 | Here is where the processing itself takes place. 93 | """ 94 | dem_layer = self.parameterAsRasterLayer( 95 | parameters, 96 | self.INPUT, 97 | context 98 | ) 99 | method_enum = int(self.parameterAsEnum( 100 | parameters, 101 | self.METHOD, 102 | context 103 | )) 104 | method = self.method_options[method_enum] 105 | dem_out_path = (self.parameterAsOutputLayer( 106 | parameters, 107 | self.OUTPUT, 108 | context, 109 | )) 110 | 111 | dem_path = str(dem_layer.source()) 112 | 113 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 114 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 115 | dem_arr = dict_arr_dem["array"] 116 | no_data = dict_arr_dem["no_data"] 117 | 118 | dem_arr[dem_arr == no_data] = np.nan 119 | 120 | dem_out = rvt.vis.fill_where_nan(dem=dem_arr, method=method) 121 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=dem_out_path, 122 | out_raster_arr=dem_out, e_type=6, no_data=np.nan) 123 | 124 | result = {self.OUTPUT: dem_out_path} 125 | return result 126 | 127 | 128 | class RVTFillNoDataIDW(QgsProcessingAlgorithm): 129 | """ 130 | RVT Fill no data Inverse Distance Weighting interpolation. 131 | """ 132 | # processing function parameters 133 | INPUT = 'INPUT' 134 | RADIUS = 'RADIUS' 135 | POWER = 'POWER' 136 | OUTPUT = 'OUTPUT' 137 | 138 | def tr(self, string): 139 | """ 140 | Returns a translatable string with the self.tr() function. 141 | """ 142 | return QCoreApplication.translate('Processing', string) 143 | 144 | def createInstance(self): 145 | return RVTFillNoDataIDW() 146 | 147 | def name(self): 148 | """ 149 | Returns the algorithm name, used for identifying the algorithm. This 150 | string should be fixed for the algorithm, and must not be localised. 151 | The name should be unique within each provider. Names should contain 152 | lowercase alphanumeric characters only and no spaces or other 153 | formatting characters. 154 | """ 155 | return 'rvt_fill_no_data_idw' 156 | 157 | def displayName(self): 158 | """ 159 | Returns the translated algorithm name, which should be used for any 160 | user-visible display of the algorithm name. 161 | """ 162 | return self.tr('RVT Fill no-data IDW') 163 | 164 | def shortHelpString(self): 165 | """ 166 | Returns a localised short helper string for the algorithm. This string 167 | should provide a basic description about what the algorithm does and the 168 | parameters and outputs associated with it.. 169 | """ 170 | return self.tr("Relief visualization toolbox, Function to fill no data. Note: DEM has to have defined no-data!") 171 | 172 | def initAlgorithm(self, config=None): 173 | """ 174 | Here we define the inputs and output of the algorithm, along 175 | with some other properties. 176 | """ 177 | self.addParameter( 178 | QgsProcessingParameterRasterLayer( 179 | self.INPUT, 180 | self.tr('Input DEM raster layer'), 181 | [QgsProcessing.TypeRaster] 182 | ) 183 | ) 184 | self.addParameter( 185 | QgsProcessingParameterNumber( 186 | name="RADIUS", 187 | description="Search radius [pixels]", 188 | type=QgsProcessingParameterNumber.Integer, 189 | defaultValue=20, 190 | minValue=1, 191 | maxValue=200 192 | ) 193 | ) 194 | self.addParameter( 195 | QgsProcessingParameterNumber( 196 | name="POWER", 197 | description="Power", 198 | type=QgsProcessingParameterNumber.Double, 199 | defaultValue=2, 200 | minValue=0.2, 201 | maxValue=10 202 | ) 203 | ) 204 | self.addParameter( 205 | QgsProcessingParameterRasterDestination( 206 | self.OUTPUT, 207 | self.tr('Output visualization raster layer') 208 | ) 209 | ) 210 | 211 | def processAlgorithm(self, parameters, context, feedback): 212 | """ 213 | Here is where the processing itself takes place. 214 | """ 215 | dem_layer = self.parameterAsRasterLayer( 216 | parameters, 217 | self.INPUT, 218 | context 219 | ) 220 | radius = int(self.parameterAsInt( 221 | parameters, 222 | self.RADIUS, 223 | context 224 | )) 225 | power = float(self.parameterAsDouble( 226 | parameters, 227 | self.POWER, 228 | context 229 | )) 230 | dem_out_path = (self.parameterAsOutputLayer( 231 | parameters, 232 | self.OUTPUT, 233 | context, 234 | )) 235 | 236 | dem_path = str(dem_layer.source()) 237 | 238 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 239 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 240 | dem_arr = dict_arr_dem["array"] 241 | no_data = dict_arr_dem["no_data"] 242 | 243 | dem_arr[dem_arr == no_data] = np.nan 244 | 245 | dem_out = rvt.vis.fill_where_nan(dem=dem_arr, method=f"idw_{radius}_{power}") 246 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=dem_out_path, 247 | out_raster_arr=dem_out, e_type=6, no_data=np.nan) 248 | 249 | result = {self.OUTPUT: dem_out_path} 250 | return result 251 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #/*************************************************************************** 2 | # QRVT 3 | # 4 | # RVT plugin lets you compute different visualizations from raster DEM. 5 | # ------------------- 6 | # begin : 2020-10-12 7 | # git sha : $Format:%H$ 8 | # copyright : © Copyright 2020 ZRC SAZU and University of Ljubljana 9 | # email : ziga.kokalj@zrc-sazu.si 10 | # ***************************************************************************/ 11 | # 12 | #/*************************************************************************** 13 | # * * 14 | # * This program is free software; you can redistribute it and/or modify * 15 | # * it under the terms of the GNU General Public License as published by * 16 | # * the Free Software Foundation; either version 2 of the License, or * 17 | # * (at your option) any later version. * 18 | # * * 19 | # ***************************************************************************/ 20 | 21 | ################################################# 22 | # Edit the following to match your sources lists 23 | ################################################# 24 | 25 | 26 | #Add iso code for any locales you want to support here (space separated) 27 | # default is no locales 28 | # LOCALES = af 29 | LOCALES = 30 | 31 | # If locales are enabled, set the name of the lrelease binary on your system. If 32 | # you have trouble compiling the translations, you may have to specify the full path to 33 | # lrelease 34 | #LRELEASE = lrelease 35 | #LRELEASE = lrelease-qt4 36 | 37 | 38 | # translation 39 | SOURCES = \ 40 | __init__.py \ 41 | qrvt.py qrvt_dialog.py 42 | 43 | PLUGINNAME = rvt-qgis 44 | 45 | PY_FILES = \ 46 | __init__.py \ 47 | qrvt.py qrvt_dialog.py \ 48 | rvt\__init__.py \ 49 | rvt\default.py rvt\blend.py \ 50 | processing_provider\provider.py 51 | 52 | 53 | UI_FILES = qrvt_dialog_base.ui qrvt_dialog_about.ui 54 | 55 | EXTRAS = metadata.txt icon.png loading.gif Makefile 56 | 57 | EXTRA_DIRS = rvt processing_provider settings i18n 58 | 59 | COMPILED_RESOURCE_FILES = resources.py 60 | 61 | PEP8EXCLUDE=pydev,resources.py,conf.py,third_party,ui 62 | 63 | # QGISDIR points to the location where your plugin should be installed. 64 | # This varies by platform, relative to your HOME directory: 65 | # * Linux: 66 | # .local/share/QGIS/QGIS3/profiles/default/python/plugins/ 67 | # * Mac OS X: 68 | # Library/Application Support/QGIS/QGIS3/profiles/default/python/plugins 69 | # * Windows: 70 | # AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins' 71 | 72 | QGISDIR=C:\Users\Uporabnik\AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins 73 | 74 | ################################################# 75 | # Normally you would not need to edit below here 76 | ################################################# 77 | 78 | HELP = help/build/html 79 | 80 | PLUGIN_UPLOAD = $(c)/plugin_upload.py 81 | 82 | RESOURCE_SRC=$(shell grep '^ *@@g;s/.*>//g' | tr '\n' ' ') 83 | 84 | .PHONY: default 85 | default: 86 | @echo While you can use make to build and deploy your plugin, pb_tool 87 | @echo is a much better solution. 88 | @echo A Python script, pb_tool provides platform independent management of 89 | @echo your plugins and runs anywhere. 90 | @echo You can install pb_tool using: pip install pb_tool 91 | @echo See https://g-sherman.github.io/plugin_build_tool/ for info. 92 | 93 | compile: $(COMPILED_RESOURCE_FILES) 94 | 95 | %.py : %.qrc $(RESOURCES_SRC) 96 | pyrcc5 -o $*.py $< 97 | 98 | %.qm : %.ts 99 | $(LRELEASE) $< 100 | 101 | test: compile transcompile 102 | @echo 103 | @echo "----------------------" 104 | @echo "Regression Test Suite" 105 | @echo "----------------------" 106 | 107 | @# Preceding dash means that make will continue in case of errors 108 | @-export PYTHONPATH=`pwd`:$(PYTHONPATH); \ 109 | export QGIS_DEBUG=0; \ 110 | export QGIS_LOG_FILE=/dev/null; \ 111 | nosetests -v --with-id --with-coverage --cover-package=. \ 112 | 3>&1 1>&2 2>&3 3>&- || true 113 | @echo "----------------------" 114 | @echo "If you get a 'no module named qgis.core error, try sourcing" 115 | @echo "the helper script we have provided first then run make test." 116 | @echo "e.g. source run-env-linux.sh ; make test" 117 | @echo "----------------------" 118 | 119 | deploy: compile doc transcompile 120 | @echo 121 | @echo "------------------------------------------" 122 | @echo "Deploying plugin to your .qgis2 directory." 123 | @echo "------------------------------------------" 124 | # The deploy target only works on unix like operating system where 125 | # the Python plugin directory is located at: 126 | # $HOME/$(QGISDIR)/python/plugins 127 | mkdir -p $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 128 | cp -vf $(PY_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 129 | cp -vf $(UI_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 130 | cp -vf $(COMPILED_RESOURCE_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 131 | cp -vf $(EXTRAS) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 132 | cp -vfr i18n $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 133 | cp -vfr $(HELP) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)/help 134 | # Copy extra directories if any 135 | (foreach EXTRA_DIR,(EXTRA_DIRS), cp -R (EXTRA_DIR) (HOME)/(QGISDIR)/python/plugins/(PLUGINNAME)/;) 136 | 137 | 138 | # The dclean target removes compiled python files from plugin directory 139 | # also deletes any .git entry 140 | dclean: 141 | @echo 142 | @echo "-----------------------------------" 143 | @echo "Removing any compiled python files." 144 | @echo "-----------------------------------" 145 | find $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) -iname "*.pyc" -delete 146 | find $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) -iname ".git" -prune -exec rm -Rf {} \; 147 | 148 | 149 | derase: 150 | @echo 151 | @echo "-------------------------" 152 | @echo "Removing deployed plugin." 153 | @echo "-------------------------" 154 | rm -Rf $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) 155 | 156 | zip: deploy dclean 157 | @echo 158 | @echo "---------------------------" 159 | @echo "Creating plugin zip bundle." 160 | @echo "---------------------------" 161 | # The zip target deploys the plugin and creates a zip file with the deployed 162 | # content. You can then upload the zip file on http://plugins.qgis.org 163 | rm -f $(PLUGINNAME).zip 164 | cd $(HOME)/$(QGISDIR)/python/plugins; zip -9r $(CURDIR)/$(PLUGINNAME).zip $(PLUGINNAME) 165 | 166 | package: compile 167 | # Create a zip package of the plugin named $(PLUGINNAME).zip. 168 | # This requires use of git (your plugin development directory must be a 169 | # git repository). 170 | # To use, pass a valid commit or tag as follows: 171 | # make package VERSION=Version_0.3.2 172 | @echo 173 | @echo "------------------------------------" 174 | @echo "Exporting plugin to zip package. " 175 | @echo "------------------------------------" 176 | rm -f $(PLUGINNAME).zip 177 | git archive --prefix=$(PLUGINNAME)/ -o $(PLUGINNAME).zip $(VERSION) 178 | echo "Created package: $(PLUGINNAME).zip" 179 | 180 | upload: zip 181 | @echo 182 | @echo "-------------------------------------" 183 | @echo "Uploading plugin to QGIS Plugin repo." 184 | @echo "-------------------------------------" 185 | $(PLUGIN_UPLOAD) $(PLUGINNAME).zip 186 | 187 | transup: 188 | @echo 189 | @echo "------------------------------------------------" 190 | @echo "Updating translation files with any new strings." 191 | @echo "------------------------------------------------" 192 | @chmod +x scripts/update-strings.sh 193 | @scripts/update-strings.sh $(LOCALES) 194 | 195 | transcompile: 196 | @echo 197 | @echo "----------------------------------------" 198 | @echo "Compiled translation files to .qm files." 199 | @echo "----------------------------------------" 200 | @chmod +x scripts/compile-strings.sh 201 | @scripts/compile-strings.sh $(LRELEASE) $(LOCALES) 202 | 203 | transclean: 204 | @echo 205 | @echo "------------------------------------" 206 | @echo "Removing compiled translation files." 207 | @echo "------------------------------------" 208 | rm -f i18n/*.qm 209 | 210 | clean: 211 | @echo 212 | @echo "------------------------------------" 213 | @echo "Removing uic and rcc generated files" 214 | @echo "------------------------------------" 215 | rm $(COMPILED_UI_FILES) $(COMPILED_RESOURCE_FILES) 216 | 217 | doc: 218 | @echo 219 | @echo "------------------------------------" 220 | @echo "Building documentation using sphinx." 221 | @echo "------------------------------------" 222 | cd help; make html 223 | 224 | pylint: 225 | @echo 226 | @echo "-----------------" 227 | @echo "Pylint violations" 228 | @echo "-----------------" 229 | @pylint --reports=n --rcfile=pylintrc . || true 230 | @echo 231 | @echo "----------------------" 232 | @echo "If you get a 'no module named qgis.core' error, try sourcing" 233 | @echo "the helper script we have provided first then run make pylint." 234 | @echo "e.g. source run-env-linux.sh ; make pylint" 235 | @echo "----------------------" 236 | 237 | 238 | # Run pep8 style checking 239 | #http://pypi.python.org/pypi/pep8 240 | pep8: 241 | @echo 242 | @echo "-----------" 243 | @echo "PEP8 issues" 244 | @echo "-----------" 245 | @pep8 --repeat --ignore=E203,E121,E122,E123,E124,E125,E126,E127,E128 --exclude $(PEP8EXCLUDE) . || true 246 | @echo "-----------" 247 | @echo "Ignored in PEP8 check:" 248 | @echo $(PEP8EXCLUDE) 249 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Specify a configuration file. 4 | #rcfile= 5 | 6 | # Python code to execute, usually for sys.path manipulation such as 7 | # pygtk.require(). 8 | #init-hook= 9 | 10 | # Profiled execution. 11 | profile=no 12 | 13 | # Add files or directories to the blacklist. They should be base names, not 14 | # paths. 15 | ignore=CVS 16 | 17 | # Pickle collected data for later comparisons. 18 | persistent=yes 19 | 20 | # List of plugins (as comma separated values of python modules names) to load, 21 | # usually to register additional checkers. 22 | load-plugins= 23 | 24 | 25 | [MESSAGES CONTROL] 26 | 27 | # Enable the message, report, category or checker with the given id(s). You can 28 | # either give multiple identifier separated by comma (,) or put this option 29 | # multiple time. See also the "--disable" option for examples. 30 | #enable= 31 | 32 | # Disable the message, report, category or checker with the given id(s). You 33 | # can either give multiple identifiers separated by comma (,) or put this 34 | # option multiple times (only on the command line, not in the configuration 35 | # file where it should appear only once).You can also use "--disable=all" to 36 | # disable everything first and then reenable specific checks. For example, if 37 | # you want to run only the similarities checker, you can use "--disable=all 38 | # --enable=similarities". If you want to run only the classes checker, but have 39 | # no Warning level messages displayed, use"--disable=all --enable=classes 40 | # --disable=W" 41 | # see http://stackoverflow.com/questions/21487025/pylint-locally-defined-disables-still-give-warnings-how-to-suppress-them 42 | disable=locally-disabled,C0103 43 | 44 | 45 | [REPORTS] 46 | 47 | # Set the output format. Available formats are text, parseable, colorized, msvs 48 | # (visual studio) and html. You can also give a reporter class, eg 49 | # mypackage.mymodule.MyReporterClass. 50 | output-format=text 51 | 52 | # Put messages in a separate file for each module / package specified on the 53 | # command line instead of printing them on stdout. Reports (if any) will be 54 | # written in a file name "pylint_global.[txt|html]". 55 | files-output=no 56 | 57 | # Tells whether to display a full report or only the messages 58 | reports=yes 59 | 60 | # Python expression which should return a note less than 10 (10 is the highest 61 | # note). You have access to the variables errors warning, statement which 62 | # respectively contain the number of errors / warnings messages and the total 63 | # number of statements analyzed. This is used by the global evaluation report 64 | # (RP0004). 65 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 66 | 67 | # Add a comment according to your evaluation note. This is used by the global 68 | # evaluation report (RP0004). 69 | comment=no 70 | 71 | # Template used to display messages. This is a python new-style format string 72 | # used to format the message information. See doc for all details 73 | #msg-template= 74 | 75 | 76 | [BASIC] 77 | 78 | # Required attributes for module, separated by a comma 79 | required-attributes= 80 | 81 | # List of builtins function names that should not be used, separated by a comma 82 | bad-functions=map,filter,apply,input 83 | 84 | # Regular expression which should only match correct module names 85 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 86 | 87 | # Regular expression which should only match correct module level names 88 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 89 | 90 | # Regular expression which should only match correct class names 91 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 92 | 93 | # Regular expression which should only match correct function names 94 | function-rgx=[a-z_][a-z0-9_]{2,30}$ 95 | 96 | # Regular expression which should only match correct method names 97 | method-rgx=[a-z_][a-z0-9_]{2,30}$ 98 | 99 | # Regular expression which should only match correct instance attribute names 100 | attr-rgx=[a-z_][a-z0-9_]{2,30}$ 101 | 102 | # Regular expression which should only match correct argument names 103 | argument-rgx=[a-z_][a-z0-9_]{2,30}$ 104 | 105 | # Regular expression which should only match correct variable names 106 | variable-rgx=[a-z_][a-z0-9_]{2,30}$ 107 | 108 | # Regular expression which should only match correct attribute names in class 109 | # bodies 110 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 111 | 112 | # Regular expression which should only match correct list comprehension / 113 | # generator expression variable names 114 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 115 | 116 | # Good variable names which should always be accepted, separated by a comma 117 | good-names=i,j,k,ex,Run,_ 118 | 119 | # Bad variable names which should always be refused, separated by a comma 120 | bad-names=foo,bar,baz,toto,tutu,tata 121 | 122 | # Regular expression which should only match function or class names that do 123 | # not require a docstring. 124 | no-docstring-rgx=__.*__ 125 | 126 | # Minimum line length for functions/classes that require docstrings, shorter 127 | # ones are exempt. 128 | docstring-min-length=-1 129 | 130 | 131 | [MISCELLANEOUS] 132 | 133 | # List of note tags to take in consideration, separated by a comma. 134 | notes=FIXME,XXX,TODO 135 | 136 | 137 | [TYPECHECK] 138 | 139 | # Tells whether missing members accessed in mixin class should be ignored. A 140 | # mixin class is detected if its name ends with "mixin" (case insensitive). 141 | ignore-mixin-members=yes 142 | 143 | # List of classes names for which member attributes should not be checked 144 | # (useful for classes with attributes dynamically set). 145 | ignored-classes=SQLObject 146 | 147 | # When zope mode is activated, add a predefined set of Zope acquired attributes 148 | # to generated-members. 149 | zope=no 150 | 151 | # List of members which are set dynamically and missed by pylint inference 152 | # system, and so shouldn't trigger E0201 when accessed. Python regular 153 | # expressions are accepted. 154 | generated-members=REQUEST,acl_users,aq_parent 155 | 156 | 157 | [VARIABLES] 158 | 159 | # Tells whether we should check for unused import in __init__ files. 160 | init-import=no 161 | 162 | # A regular expression matching the beginning of the name of dummy variables 163 | # (i.e. not used). 164 | dummy-variables-rgx=_$|dummy 165 | 166 | # List of additional names supposed to be defined in builtins. Remember that 167 | # you should avoid to define new builtins when possible. 168 | additional-builtins= 169 | 170 | 171 | [FORMAT] 172 | 173 | # Maximum number of characters on a single line. 174 | max-line-length=80 175 | 176 | # Regexp for a line that is allowed to be longer than the limit. 177 | ignore-long-lines=^\s*(# )??$ 178 | 179 | # Allow the body of an if to be on the same line as the test if there is no 180 | # else. 181 | single-line-if-stmt=no 182 | 183 | # List of optional constructs for which whitespace checking is disabled 184 | no-space-check=trailing-comma,dict-separator 185 | 186 | # Maximum number of lines in a module 187 | max-module-lines=1000 188 | 189 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 190 | # tab). 191 | indent-string=' ' 192 | 193 | 194 | [SIMILARITIES] 195 | 196 | # Minimum lines number of a similarity. 197 | min-similarity-lines=4 198 | 199 | # Ignore comments when computing similarities. 200 | ignore-comments=yes 201 | 202 | # Ignore docstrings when computing similarities. 203 | ignore-docstrings=yes 204 | 205 | # Ignore imports when computing similarities. 206 | ignore-imports=no 207 | 208 | 209 | [IMPORTS] 210 | 211 | # Deprecated modules which should not be used, separated by a comma 212 | deprecated-modules=regsub,TERMIOS,Bastion,rexec 213 | 214 | # Create a graph of every (i.e. internal and external) dependencies in the 215 | # given file (report RP0402 must not be disabled) 216 | import-graph= 217 | 218 | # Create a graph of external dependencies in the given file (report RP0402 must 219 | # not be disabled) 220 | ext-import-graph= 221 | 222 | # Create a graph of internal dependencies in the given file (report RP0402 must 223 | # not be disabled) 224 | int-import-graph= 225 | 226 | 227 | [DESIGN] 228 | 229 | # Maximum number of arguments for function / method 230 | max-args=5 231 | 232 | # Argument names that match this expression will be ignored. Default to name 233 | # with leading underscore 234 | ignored-argument-names=_.* 235 | 236 | # Maximum number of locals for function / method body 237 | max-locals=15 238 | 239 | # Maximum number of return / yield for function / method body 240 | max-returns=6 241 | 242 | # Maximum number of branch for function / method body 243 | max-branches=12 244 | 245 | # Maximum number of statements in function / method body 246 | max-statements=50 247 | 248 | # Maximum number of parents for a class (see R0901). 249 | max-parents=7 250 | 251 | # Maximum number of attributes for a class (see R0902). 252 | max-attributes=7 253 | 254 | # Minimum number of public methods for a class (see R0903). 255 | min-public-methods=2 256 | 257 | # Maximum number of public methods for a class (see R0904). 258 | max-public-methods=20 259 | 260 | 261 | [CLASSES] 262 | 263 | # List of interface methods to ignore, separated by a comma. This is used for 264 | # instance to not check methods defines in Zope's Interface base class. 265 | ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by 266 | 267 | # List of method names used to declare (i.e. assign) instance attributes. 268 | defining-attr-methods=__init__,__new__,setUp 269 | 270 | # List of valid names for the first argument in a class method. 271 | valid-classmethod-first-arg=cls 272 | 273 | # List of valid names for the first argument in a metaclass class method. 274 | valid-metaclass-classmethod-first-arg=mcs 275 | 276 | 277 | [EXCEPTIONS] 278 | 279 | # Exceptions that will emit a warning when being caught. Defaults to 280 | # "Exception" 281 | overgeneral-exceptions=Exception 282 | -------------------------------------------------------------------------------- /processing_provider/rvt_blender.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.blend 15 | import rvt.vis 16 | import os 17 | 18 | 19 | class RVTBlender(QgsProcessingAlgorithm): 20 | """ 21 | RVT Blender. 22 | """ 23 | # processing function parameters 24 | INPUT = 'INPUT' 25 | BLEND_COMBINATION = 'BLEND_COMBINATION' 26 | TERRAIN_TYPE = 'TERRAIN_TYPE' 27 | OUTPUT = 'OUTPUT' 28 | NOISE_REMOVE = "NOISE_REMOVE" 29 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 30 | 31 | # read default blender combinations from settings/json, read default terrain settings from settings/json 32 | default_blender_combinations_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 33 | "settings", "default_blender_combinations.json")) 34 | terrains_settings_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 35 | "settings", "default_terrains_settings.json")) 36 | combinations = rvt.blend.BlenderCombinations() 37 | terrains_settings = rvt.blend.TerrainsSettings() 38 | combinations.read_from_file(default_blender_combinations_path) 39 | terrains_settings.read_from_file(terrains_settings_path) 40 | 41 | # find out values for comboboxes 42 | combinations_names = [] 43 | terrains_sett_names = [] 44 | for combination in combinations.combinations: 45 | combinations_names.append(combination.name) 46 | for terrain_sett in terrains_settings.terrains_settings: 47 | terrains_sett_names.append(terrain_sett.name) 48 | 49 | def tr(self, string): 50 | """ 51 | Returns a translatable string with the self.tr() function. 52 | """ 53 | return QCoreApplication.translate('Processing', string) 54 | 55 | def createInstance(self): 56 | return RVTBlender() 57 | 58 | def name(self): 59 | """ 60 | Returns the algorithm name, used for identifying the algorithm. This 61 | string should be fixed for the algorithm, and must not be localised. 62 | The name should be unique within each provider. Names should contain 63 | lowercase alphanumeric characters only and no spaces or other 64 | formatting characters. 65 | """ 66 | return 'rvt_blender' 67 | 68 | def displayName(self): 69 | """ 70 | Returns the translated algorithm name, which should be used for any 71 | user-visible display of the algorithm name. 72 | """ 73 | return self.tr('RVT Blender') 74 | 75 | def shortHelpString(self): 76 | """ 77 | Returns a localised short helper string for the algorithm. This string 78 | should provide a basic description about what the algorithm does and the 79 | parameters and outputs associated with it.. 80 | """ 81 | return self.tr("Relief visualization toolbox, Blender. Calculates blended visualization.") 82 | 83 | def initAlgorithm(self, config=None): 84 | """ 85 | Here we define the inputs and output of the algorithm, along 86 | with some other properties. 87 | """ 88 | self.addParameter( 89 | QgsProcessingParameterRasterLayer( 90 | self.INPUT, 91 | self.tr('Input DEM raster layer'), 92 | [QgsProcessing.TypeRaster] 93 | ) 94 | ) 95 | self.addParameter( 96 | QgsProcessingParameterEnum( 97 | name="BLEND_COMBINATION", 98 | description="Combination", 99 | options=self.combinations_names 100 | ) 101 | ) 102 | self.addParameter( 103 | QgsProcessingParameterEnum( 104 | name="TERRAIN_TYPE", 105 | description="Terrain type", 106 | options=self.terrains_sett_names 107 | ) 108 | ) 109 | self.addParameter( 110 | QgsProcessingParameterBoolean( 111 | name="SAVE_AS_8BIT", 112 | description="Save as 8bit raster", 113 | defaultValue=False 114 | ) 115 | ) 116 | self.addParameter( 117 | QgsProcessingParameterRasterDestination( 118 | self.OUTPUT, 119 | self.tr('Output visualization raster layer') 120 | ) 121 | ) 122 | 123 | def processAlgorithm(self, parameters, context, feedback): 124 | """ 125 | Here is where the processing itself takes place. 126 | """ 127 | dem_layer = self.parameterAsRasterLayer( 128 | parameters, 129 | self.INPUT, 130 | context 131 | ) 132 | combination_name = self.combinations_names[int(self.parameterAsEnum( 133 | parameters, 134 | self.BLEND_COMBINATION, 135 | context 136 | ))] 137 | terrain_name = self.terrains_sett_names[int(self.parameterAsEnum( 138 | parameters, 139 | self.TERRAIN_TYPE, 140 | context 141 | ))] 142 | save_8bit = bool(self.parameterAsBool( 143 | parameters, 144 | self.SAVE_AS_8BIT, 145 | context 146 | )) 147 | visualization_path = (self.parameterAsOutputLayer( 148 | parameters, 149 | self.OUTPUT, 150 | context, 151 | )) 152 | 153 | dem_path = str(dem_layer.source()) 154 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 155 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 156 | dem_arr = dict_arr_dem["array"] 157 | no_data = dict_arr_dem["no_data"] 158 | 159 | #if save_8bit = True save_float is False, can only output one 160 | save_float = True 161 | if save_8bit: 162 | save_float = False 163 | 164 | # advanced custom combinations (hard coded) blending (which can't be created in dialog) 165 | if combination_name == "Archaeological combined (VAT combined)": 166 | vat_combination_json_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 167 | "settings", "blender_VAT.json")) 168 | default_1 = rvt.default.DefaultValues() # VAT general 169 | default_2 = rvt.default.DefaultValues() # VAT flat 170 | 171 | vat_combination_1 = rvt.blend.BlenderCombination() # VAT general 172 | vat_combination_2 = rvt.blend.BlenderCombination() # VAT flat 173 | vat_combination_1.read_from_file(vat_combination_json_path) 174 | vat_combination_2.read_from_file(vat_combination_json_path) 175 | terrain_1 = self.terrains_settings.select_terrain_settings_by_name("general") # VAT general 176 | terrain_2 = self.terrains_settings.select_terrain_settings_by_name("flat") # VAT flat 177 | terrain_1.apply_terrain(default=default_1, combination=vat_combination_1) # VAT general 178 | terrain_2.apply_terrain(default=default_2, combination=vat_combination_2) # VAT flat 179 | 180 | dict_arr_res_nd = rvt.default.get_raster_arr(raster_path=dem_path) 181 | vat_combination_1.add_dem_arr(dem_arr=dict_arr_res_nd["array"], 182 | dem_resolution=dict_arr_res_nd["resolution"][0]) 183 | vat_arr_1 = vat_combination_1.render_all_images(default=default_1, no_data=dict_arr_res_nd["no_data"]) 184 | vat_combination_2.add_dem_arr(dem_arr=dict_arr_res_nd["array"], 185 | dem_resolution=dict_arr_res_nd["resolution"][0]) 186 | vat_arr_2 = vat_combination_2.render_all_images(default=default_2, no_data=dict_arr_res_nd["no_data"]) 187 | 188 | # blend VAT general and VAT flat together 189 | combination = rvt.blend.BlenderCombination() 190 | combination.create_layer(vis_method="VAT general", image=vat_arr_1, normalization="Value", minimum=0, 191 | maximum=1, blend_mode="Normal", opacity=50) 192 | combination.create_layer(vis_method="VAT flat", image=vat_arr_2, normalization="Value", minimum=0, 193 | maximum=1, blend_mode="Normal", opacity=100) 194 | combination.add_dem_path(dem_path=dem_path) 195 | combination.render_all_images(save_render_path=visualization_path, save_visualizations=False, 196 | save_float=save_float, save_8bit=save_8bit, 197 | no_data=no_data) 198 | elif combination_name == "enhanced Multi-Scale Topographic Position version 3": 199 | dict_arr_res_nd = rvt.default.get_raster_arr(raster_path=dem_path) 200 | e3mstp_arr = rvt.blend.e3mstp(dem=dict_arr_res_nd["array"], resolution=dict_arr_res_nd["resolution"][0], 201 | no_data=dict_arr_res_nd["no_data"]) 202 | if save_float: 203 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 204 | out_raster_arr=e3mstp_arr, no_data=np.nan, e_type=6) 205 | else: 206 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 207 | out_raster_arr=rvt.vis.byte_scale(e3mstp_arr, c_min=0.0, c_max=1.0), 208 | no_data=np.nan, e_type=1) 209 | # normal combination blending 210 | else: 211 | # create default 212 | default = rvt.default.DefaultValues() 213 | 214 | # create combination 215 | combination = self.combinations.select_combination_by_name(combination_name) 216 | # apply terrain settings 217 | terrain_sett = self.terrains_settings.select_terrain_settings_by_name(terrain_name) 218 | terrain_sett.apply_terrain(default, combination) 219 | combination.add_dem_arr(dem_arr=dem_arr, dem_resolution=resolution[0]) 220 | combination.add_dem_path(dem_path) 221 | combination.render_all_images(default=default, save_visualizations=False, 222 | save_render_path=visualization_path, 223 | save_float=save_float, save_8bit=save_8bit, no_data=no_data) 224 | 225 | result = {self.OUTPUT: visualization_path} 226 | return result 227 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /qrvt_dialog_about.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'qrvt_dialog_about.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.1 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_RvtAbout(object): 15 | def setupUi(self, RvtAbout): 16 | RvtAbout.setObjectName("RvtAbout") 17 | RvtAbout.resize(600, 700) 18 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(RvtAbout) 19 | self.verticalLayout_3.setObjectName("verticalLayout_3") 20 | self.verticalLayout = QtWidgets.QVBoxLayout() 21 | self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize) 22 | self.verticalLayout.setObjectName("verticalLayout") 23 | self.label = QtWidgets.QLabel(RvtAbout) 24 | self.label.setMinimumSize(QtCore.QSize(0, 0)) 25 | self.label.setMaximumSize(QtCore.QSize(16777215, 16777215)) 26 | font = QtGui.QFont() 27 | font.setPointSize(10) 28 | font.setBold(True) 29 | font.setWeight(75) 30 | self.label.setFont(font) 31 | self.label.setObjectName("label") 32 | self.verticalLayout.addWidget(self.label) 33 | self.horizontalLayout = QtWidgets.QHBoxLayout() 34 | self.horizontalLayout.setObjectName("horizontalLayout") 35 | self.label_2 = QtWidgets.QLabel(RvtAbout) 36 | self.label_2.setMinimumSize(QtCore.QSize(0, 0)) 37 | self.label_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) 38 | self.label_2.setObjectName("label_2") 39 | self.horizontalLayout.addWidget(self.label_2) 40 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 41 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 42 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 43 | self.horizontalLayout_2.addItem(spacerItem) 44 | self.verticalLayout_2 = QtWidgets.QVBoxLayout() 45 | self.verticalLayout_2.setObjectName("verticalLayout_2") 46 | self.label_9 = QtWidgets.QLabel(RvtAbout) 47 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 48 | sizePolicy.setHorizontalStretch(0) 49 | sizePolicy.setVerticalStretch(0) 50 | sizePolicy.setHeightForWidth(self.label_9.sizePolicy().hasHeightForWidth()) 51 | self.label_9.setSizePolicy(sizePolicy) 52 | self.label_9.setMinimumSize(QtCore.QSize(240, 147)) 53 | self.label_9.setMaximumSize(QtCore.QSize(240, 147)) 54 | self.label_9.setText("") 55 | self.label_9.setPixmap(QtGui.QPixmap("icon.png")) 56 | self.label_9.setScaledContents(True) 57 | self.label_9.setObjectName("label_9") 58 | self.verticalLayout_2.addWidget(self.label_9) 59 | self.horizontalLayout_2.addLayout(self.verticalLayout_2) 60 | spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 61 | self.horizontalLayout_2.addItem(spacerItem1) 62 | self.horizontalLayout.addLayout(self.horizontalLayout_2) 63 | self.verticalLayout.addLayout(self.horizontalLayout) 64 | self.label_3 = QtWidgets.QLabel(RvtAbout) 65 | self.label_3.setMinimumSize(QtCore.QSize(0, 0)) 66 | self.label_3.setMaximumSize(QtCore.QSize(16777215, 16777215)) 67 | self.label_3.setOpenExternalLinks(True) 68 | self.label_3.setObjectName("label_3") 69 | self.verticalLayout.addWidget(self.label_3) 70 | self.label_4 = QtWidgets.QLabel(RvtAbout) 71 | self.label_4.setMinimumSize(QtCore.QSize(0, 0)) 72 | self.label_4.setMaximumSize(QtCore.QSize(16777215, 16777215)) 73 | self.label_4.setObjectName("label_4") 74 | self.verticalLayout.addWidget(self.label_4) 75 | self.label_5 = QtWidgets.QLabel(RvtAbout) 76 | self.label_5.setMinimumSize(QtCore.QSize(0, 0)) 77 | self.label_5.setMaximumSize(QtCore.QSize(16777215, 16777215)) 78 | self.label_5.setObjectName("label_5") 79 | self.verticalLayout.addWidget(self.label_5) 80 | self.label_7 = QtWidgets.QLabel(RvtAbout) 81 | self.label_7.setObjectName("label_7") 82 | self.verticalLayout.addWidget(self.label_7) 83 | self.label_8 = QtWidgets.QLabel(RvtAbout) 84 | self.label_8.setOpenExternalLinks(True) 85 | self.label_8.setObjectName("label_8") 86 | self.verticalLayout.addWidget(self.label_8) 87 | self.label_6 = QtWidgets.QLabel(RvtAbout) 88 | self.label_6.setObjectName("label_6") 89 | self.verticalLayout.addWidget(self.label_6) 90 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 91 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 92 | spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 93 | self.horizontalLayout_3.addItem(spacerItem2) 94 | self.button_report_bug = QtWidgets.QPushButton(RvtAbout) 95 | self.button_report_bug.setObjectName("button_report_bug") 96 | self.horizontalLayout_3.addWidget(self.button_report_bug) 97 | spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 98 | self.horizontalLayout_3.addItem(spacerItem3) 99 | self.button_close = QtWidgets.QPushButton(RvtAbout) 100 | self.button_close.setObjectName("button_close") 101 | self.horizontalLayout_3.addWidget(self.button_close) 102 | spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 103 | self.horizontalLayout_3.addItem(spacerItem4) 104 | self.horizontalLayout_3.setStretch(0, 1) 105 | self.horizontalLayout_3.setStretch(1, 1) 106 | self.horizontalLayout_3.setStretch(2, 5) 107 | self.horizontalLayout_3.setStretch(3, 1) 108 | self.horizontalLayout_3.setStretch(4, 1) 109 | self.verticalLayout.addLayout(self.horizontalLayout_3) 110 | self.verticalLayout_3.addLayout(self.verticalLayout) 111 | 112 | self.retranslateUi(RvtAbout) 113 | QtCore.QMetaObject.connectSlotsByName(RvtAbout) 114 | 115 | def retranslateUi(self, RvtAbout): 116 | _translate = QtCore.QCoreApplication.translate 117 | RvtAbout.setWindowTitle(_translate("RvtAbout", "RvtAbout")) 118 | self.label.setText(_translate("RvtAbout", "Relief Visualization Toolbox (RVT) QGIS plugin, ver. 0.3")) 119 | self.label_2.setText(_translate("RvtAbout", "\n" 120 | "○ By:\n" 121 | "\n" 122 | "
​ ​ ​ ​ \n" 123 | "• Žiga Kokalj\n" 124 | "\n" 125 | "
​ ​ ​ ​ \n" 126 | "• Krištof Oštir\n" 127 | "\n" 128 | "
​ ​ ​ ​ \n" 129 | "• Klemen Zakšek\n" 130 | "\n" 131 | "
​ ​ ​ ​ \n" 132 | "• Peter Pehani\n" 133 | "\n" 134 | "
​ ​ ​ ​ \n" 135 | "• Klemen Čotar\n" 136 | "\n" 137 | "
​ ​ ​ ​ \n" 138 | "• Maja Somrak\n" 139 | "\n" 140 | "
​ ​ ​ ​ \n" 141 | "• Žiga Maroh (plugin author and maintainer)\n" 142 | "
\n" 143 | "<\\body>\n" 144 | "<\\html>")) 145 | self.label_3.setText(_translate("RvtAbout", "\n" 146 | "○ Online resources:
\n" 147 | "\n" 148 | "​ ​ ​ ​ • RVT QGIS plugin GitHub
\n" 149 | "\n" 150 | "​ ​ ​ ​ • RVT core python library GitHub
\n" 151 | "\n" 152 | "​ ​ ​ ​ • RVT ArcGIS Pro raster functions GitHub
\n" 153 | "\n" 154 | "​ ​ ​ ​ • Old RVT
\n" 155 | "\n" 156 | "<\\body>\n" 157 | "<\\html>")) 158 | self.label_4.setText(_translate("RvtAbout", "\n" 159 | "○ License agreement:
\n" 160 | "​ ​ ​ ​ This software is distributed without any warranty and without even the implied warranty of merchantability or fitness \n" 161 | "
​ ​ ​ ​ \n" 162 | " for a particulat pupose.\n" 163 | "
\n" 164 | "<\\body>\n" 165 | "<\\html>")) 166 | self.label_5.setText(_translate("RvtAbout", "

○ Acknowledgment:
\n" 167 | "​ ​ ​ ​ \n" 168 | "Development of RVT was partly financed by the European Commission\'s Culture Programme through \n" 169 | "
​ ​ ​ ​ \n" 170 | "the ArchaeoLandscapes Europe project and by the Slovenian Research Agency core funding No. P2-0406,\n" 171 | "
​ ​ ​ ​ \n" 172 | " and by research projects No. J6-7085 and No. J6-9395.

")) 173 | self.label_7.setText(_translate("RvtAbout", "

○ Refrences:
\n" 174 | "​ ​ ​ ​ \n" 175 | "When using tools, please cite:
​ ​ ​ ​ \n" 176 | "• Kokalj, Ž., Somrak, M. 2019. Why Not a Single Image? Combining Visualizations to Facilitate Fieldwork and\n" 177 | "
​ ​ ​ ​ ​ ​ ​ \n" 178 | " On-Screen Mapping. Remote Sensing 11(7): 747.\n" 179 | "
​ ​ ​ ​ \n" 180 | "• Zakšek, K., Oštir, K., Kokalj, Ž. 2011. Sky-View Factor as a Relief Visualization Technique.\n" 181 | "
​ ​ ​ ​ ​ ​ ​ \n" 182 | " Remote Sensing 3: 398-415.\n" 183 | "
​ ​ ​ ​ \n" 184 | "• Kokalj, Ž., Zakšek, K., Oštir, K. 2011. Application of Sky-View Factor for the Visualization of Historic Landscape \n" 185 | "
​ ​ ​ ​ ​ ​ ​ \n" 186 | "Features in Lidar-Derived Relief Models. Antiquity 85, 327: 263-273.\n" 187 | "
\n" 188 | "")) 189 | self.label_8.setText(_translate("RvtAbout", "

○ Bugs and Suggestions:
\n" 190 | "​ ​ ​ ​ \n" 191 | "Please report any bugs to Issues or to email: ziga.maroh@icloud.com\n" 192 | "
\n" 193 | "​ ​ ​ ​ \n" 194 | "Suggestions for improvments can be sent to email: ziga.kokalj@zrc-sazu.si\n" 195 | "
\n" 196 | "")) 197 | self.label_6.setText(_translate("RvtAbout", "

○ © Copyright:
\n" 198 | "​ ​ ​ ​ \n" 199 | "Research Center of the Slovenian Academy of Sciences and Arts (ZRC SAZU) and
​ ​ ​ ​ \n" 200 | " University of Ljubljana, Faculty of Civil and Geodetic Engineering (UL FGG), 2020\n" 201 | "
\n" 202 | "")) 203 | self.button_report_bug.setText(_translate("RvtAbout", "Report a bug")) 204 | self.button_close.setText(_translate("RvtAbout", "Close")) 205 | -------------------------------------------------------------------------------- /processing_provider/rvt_mstp.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QCoreApplication 2 | from qgis.core import (QgsProcessing, 3 | QgsFeatureSink, 4 | QgsProcessingException, 5 | QgsProcessingAlgorithm, 6 | QgsProcessingParameterNumber, 7 | QgsProcessingParameterRasterLayer, 8 | QgsProcessingParameterRasterDestination, 9 | QgsProcessingParameterEnum, 10 | QgsProcessingParameterBoolean) 11 | from qgis import processing 12 | import numpy as np 13 | import rvt.default 14 | import rvt.vis 15 | from rvt.default import RVTVisualization 16 | 17 | 18 | class RVTMstp(QgsProcessingAlgorithm): 19 | """ 20 | RVT Multi-scale topographic position. 21 | """ 22 | # processing function parameters 23 | INPUT = 'INPUT' 24 | VE_FACTOR = 'VE_FACTOR' 25 | LOCAL_SCALE_MIN = "LOCAL_SCALE_MIN" 26 | LOCAL_SCALE_MAX = "LOCAL_SCALE_MAX" 27 | LOCAL_SCALE_STEP = "LOCAL_SCALE_STEP" 28 | MESO_SCALE_MIN = "MESO_SCALE_MIN" 29 | MESO_SCALE_MAX = "MESO_SCALE_MAX" 30 | MESO_SCALE_STEP = "MESO_SCALE_STEP" 31 | BROAD_SCALE_MIN = "BROAD_SCALE_MIN" 32 | BROAD_SCALE_MAX = "BROAD_SCALE_MAX" 33 | BROAD_SCALE_STEP = "BROAD_SCALE_STEP" 34 | LIGHTNESS = "LIGHTNESS" 35 | SAVE_AS_8BIT = "SAVE_AS_8BIT" 36 | OUTPUT = 'OUTPUT' 37 | 38 | noise_options = ["no removal", "low", "medium", "high"] 39 | 40 | def tr(self, string): 41 | """ 42 | Returns a translatable string with the self.tr() function. 43 | """ 44 | return QCoreApplication.translate('Processing', string) 45 | 46 | def createInstance(self): 47 | return RVTMstp() 48 | 49 | def name(self): 50 | """ 51 | Returns the algorithm name, used for identifying the algorithm. This 52 | string should be fixed for the algorithm, and must not be localised. 53 | The name should be unique within each provider. Names should contain 54 | lowercase alphanumeric characters only and no spaces or other 55 | formatting characters. 56 | """ 57 | return 'rvt_mstp' 58 | 59 | def displayName(self): 60 | """ 61 | Returns the translated algorithm name, which should be used for any 62 | user-visible display of the algorithm name. 63 | """ 64 | return self.tr('RVT Multi-scale topographic position') 65 | 66 | def shortHelpString(self): 67 | """ 68 | Returns a localised short helper string for the algorithm. This string 69 | should provide a basic description about what the algorithm does and the 70 | parameters and outputs associated with it.. 71 | """ 72 | return self.tr("Relief visualization toolbox, Multi-scale topographic position. " 73 | "Calculates Multi-scale topographic position.") 74 | 75 | def initAlgorithm(self, config=None): 76 | """ 77 | Here we define the inputs and output of the algorithm, along 78 | with some other properties. 79 | """ 80 | self.addParameter( 81 | QgsProcessingParameterRasterLayer( 82 | self.INPUT, 83 | self.tr('Input DEM raster layer'), 84 | [QgsProcessing.TypeRaster] 85 | ) 86 | ) 87 | self.addParameter( 88 | QgsProcessingParameterNumber( 89 | name="VE_FACTOR", 90 | description="Vertical exaggeration factor", 91 | type=QgsProcessingParameterNumber.Double, 92 | defaultValue=1, 93 | minValue=-1000, 94 | maxValue=1000 95 | ) 96 | ) 97 | self.addParameter( 98 | QgsProcessingParameterNumber( 99 | name="LOCAL_SCALE_MIN", 100 | description="Local scale minimum radius [pixels]", 101 | type=QgsProcessingParameterNumber.Integer, 102 | defaultValue=3, 103 | minValue=1, 104 | maxValue=1000000000 105 | ) 106 | ) 107 | self.addParameter( 108 | QgsProcessingParameterNumber( 109 | name="LOCAL_SCALE_MAX", 110 | description="Local scale maximum radius [pixels]", 111 | type=QgsProcessingParameterNumber.Integer, 112 | defaultValue=21, 113 | minValue=1, 114 | maxValue=1000000000 115 | ) 116 | ) 117 | self.addParameter( 118 | QgsProcessingParameterNumber( 119 | name="LOCAL_SCALE_STEP", 120 | description="Local scale step [pixels]", 121 | type=QgsProcessingParameterNumber.Integer, 122 | defaultValue=2, 123 | minValue=1, 124 | maxValue=1000000000 125 | ) 126 | ) 127 | self.addParameter( 128 | QgsProcessingParameterNumber( 129 | name="MESO_SCALE_MIN", 130 | description="Meso scale minimum radius [pixels]", 131 | type=QgsProcessingParameterNumber.Integer, 132 | defaultValue=23, 133 | minValue=1, 134 | maxValue=1000000000 135 | ) 136 | ) 137 | self.addParameter( 138 | QgsProcessingParameterNumber( 139 | name="MESO_SCALE_MAX", 140 | description="Meso scale maximum radius [pixels]", 141 | type=QgsProcessingParameterNumber.Integer, 142 | defaultValue=203, 143 | minValue=1, 144 | maxValue=1000000000 145 | ) 146 | ) 147 | self.addParameter( 148 | QgsProcessingParameterNumber( 149 | name="MESO_SCALE_STEP", 150 | description="Meso scale step [pixels]", 151 | type=QgsProcessingParameterNumber.Integer, 152 | defaultValue=18, 153 | minValue=1, 154 | maxValue=1000000000 155 | ) 156 | ) 157 | self.addParameter( 158 | QgsProcessingParameterNumber( 159 | name="BROAD_SCALE_MIN", 160 | description="Broad scale minimum radius [pixels]", 161 | type=QgsProcessingParameterNumber.Integer, 162 | defaultValue=223, 163 | minValue=1, 164 | maxValue=1000000000 165 | ) 166 | ) 167 | self.addParameter( 168 | QgsProcessingParameterNumber( 169 | name="BROAD_SCALE_MAX", 170 | description="Broad scale maximum radius [pixels]", 171 | type=QgsProcessingParameterNumber.Integer, 172 | defaultValue=2023, 173 | minValue=1, 174 | maxValue=1000000000 175 | ) 176 | ) 177 | self.addParameter( 178 | QgsProcessingParameterNumber( 179 | name="BROAD_SCALE_STEP", 180 | description="Broad scale step [pixels]", 181 | type=QgsProcessingParameterNumber.Integer, 182 | defaultValue=180, 183 | minValue=1, 184 | maxValue=1000000000 185 | ) 186 | ) 187 | self.addParameter( 188 | QgsProcessingParameterNumber( 189 | name="LIGHTNESS", 190 | description="Lightness of image", 191 | type=QgsProcessingParameterNumber.Double, 192 | defaultValue=1.2, 193 | minValue=0.1, 194 | maxValue=5.0 195 | ) 196 | ) 197 | self.addParameter( 198 | QgsProcessingParameterBoolean( 199 | name="SAVE_AS_8BIT", 200 | description="Save as 8bit raster", 201 | defaultValue=False 202 | ) 203 | ) 204 | self.addParameter( 205 | QgsProcessingParameterRasterDestination( 206 | self.OUTPUT, 207 | self.tr('Output visualization raster layer') 208 | ) 209 | ) 210 | 211 | def processAlgorithm(self, parameters, context, feedback): 212 | """ 213 | Here is where the processing itself takes place. 214 | """ 215 | dem_layer = self.parameterAsRasterLayer( 216 | parameters, 217 | self.INPUT, 218 | context 219 | ) 220 | ve_factor = float(self.parameterAsDouble( 221 | parameters, 222 | self.VE_FACTOR, 223 | context 224 | )) 225 | local_scale_min = int(self.parameterAsInt( 226 | parameters, 227 | self.LOCAL_SCALE_MIN, 228 | context 229 | )) 230 | local_scale_max = int(self.parameterAsInt( 231 | parameters, 232 | self.LOCAL_SCALE_MAX, 233 | context 234 | )) 235 | local_scale_step = int(self.parameterAsInt( 236 | parameters, 237 | self.LOCAL_SCALE_STEP, 238 | context 239 | )) 240 | meso_scale_min = int(self.parameterAsInt( 241 | parameters, 242 | self.MESO_SCALE_MIN, 243 | context 244 | )) 245 | meso_scale_max = int(self.parameterAsInt( 246 | parameters, 247 | self.MESO_SCALE_MAX, 248 | context 249 | )) 250 | meso_scale_step = int(self.parameterAsInt( 251 | parameters, 252 | self.MESO_SCALE_STEP, 253 | context 254 | )) 255 | broad_scale_min = int(self.parameterAsInt( 256 | parameters, 257 | self.BROAD_SCALE_MIN, 258 | context 259 | )) 260 | broad_scale_max = int(self.parameterAsInt( 261 | parameters, 262 | self.BROAD_SCALE_MAX, 263 | context 264 | )) 265 | broad_scale_step = int(self.parameterAsInt( 266 | parameters, 267 | self.BROAD_SCALE_STEP, 268 | context 269 | )) 270 | lightness = int(self.parameterAsDouble( 271 | parameters, 272 | self.LIGHTNESS, 273 | context 274 | )) 275 | save_8bit = bool(self.parameterAsBool( 276 | parameters, 277 | self.SAVE_AS_8BIT, 278 | context 279 | )) 280 | visualization_path = (self.parameterAsOutputLayer( 281 | parameters, 282 | self.OUTPUT, 283 | context, 284 | )) 285 | 286 | dem_path = str(dem_layer.source()) 287 | 288 | dict_arr_dem = rvt.default.get_raster_arr(dem_path) 289 | resolution = dict_arr_dem["resolution"] # (x_res, y_res) 290 | dem_arr = dict_arr_dem["array"] 291 | no_data = dict_arr_dem["no_data"] 292 | 293 | visualization_arr = rvt.vis.mstp(dem=dem_arr, 294 | local_scale=(local_scale_min, local_scale_max, local_scale_step), 295 | meso_scale=(meso_scale_min, meso_scale_max, meso_scale_step), 296 | broad_scale=(broad_scale_min, broad_scale_max, broad_scale_step), 297 | lightness=lightness, ve_factor=ve_factor, 298 | no_data=no_data) 299 | if not save_8bit: 300 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 301 | out_raster_arr=visualization_arr, e_type=6, no_data=np.nan) 302 | else: 303 | visualization_8bit_arr = rvt.default.DefaultValues().float_to_8bit( 304 | float_arr=visualization_arr, visualization=RVTVisualization.MULTI_SCALE_TOPOGRAPHIC_POSITION 305 | ) 306 | rvt.default.save_raster(src_raster_path=dem_path, out_raster_path=visualization_path, 307 | out_raster_arr=visualization_8bit_arr, e_type=1, no_data=np.nan) 308 | 309 | result = {self.OUTPUT: visualization_path} 310 | return result 311 | --------------------------------------------------------------------------------