From cce1431fa128d9f1906be4fe17f4bbd363c1f68a Mon Sep 17 00:00:00 2001 From: dragosgontariu <161034767+dragosgontariu@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:17:14 +0300 Subject: [PATCH] Add files via upload --- gender_indicator_tool/README.html | 42 + gender_indicator_tool/README.txt | 32 + gender_indicator_tool/Style/Accessibility.qml | 426 + gender_indicator_tool/Style/Contextual.qml | 426 + gender_indicator_tool/Style/Education.qml | 426 + gender_indicator_tool/Style/Final.qml | 176 + gender_indicator_tool/Style/Individual.qml | 426 + .../Style/Insights Aggregation.qml | 940 +++ .../Style/Insights Buffer Aggregation.qml | 917 +++ .../Style/Insights Combined.qml | 171 + .../Style/Insights Population.qml | 159 + .../Style/Insights Score.qml | 162 + .../Style/Place Characterization.qml | 426 + gender_indicator_tool/WB_LOGO.qrc | 5 + gender_indicator_tool/__init__.py | 37 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1784 bytes .../gender_indicator_tool.cpython-39.pyc | Bin 0 -> 93811 bytes ...ender_indicator_tool_dialog.cpython-39.pyc | Bin 0 -> 2273 bytes .../__pycache__/resources.cpython-39.pyc | Bin 0 -> 2291 bytes .../gender_indicator_tool.py | 6964 +++++++++++++++++ .../gender_indicator_tool_dialog.py | 51 + .../gender_indicator_tool_dialog_base.ui | 4355 +++++++++++ gender_indicator_tool/icon.png | Bin 0 -> 1067 bytes gender_indicator_tool/metadata.txt | 46 + gender_indicator_tool/plugin_upload.py | 111 + gender_indicator_tool/pylintrc | 281 + gender_indicator_tool/resources.py | 131 + gender_indicator_tool/resources.qrc | 5 + 28 files changed, 16715 insertions(+) create mode 100644 gender_indicator_tool/README.html create mode 100644 gender_indicator_tool/README.txt create mode 100644 gender_indicator_tool/Style/Accessibility.qml create mode 100644 gender_indicator_tool/Style/Contextual.qml create mode 100644 gender_indicator_tool/Style/Education.qml create mode 100644 gender_indicator_tool/Style/Final.qml create mode 100644 gender_indicator_tool/Style/Individual.qml create mode 100644 gender_indicator_tool/Style/Insights Aggregation.qml create mode 100644 gender_indicator_tool/Style/Insights Buffer Aggregation.qml create mode 100644 gender_indicator_tool/Style/Insights Combined.qml create mode 100644 gender_indicator_tool/Style/Insights Population.qml create mode 100644 gender_indicator_tool/Style/Insights Score.qml create mode 100644 gender_indicator_tool/Style/Place Characterization.qml create mode 100644 gender_indicator_tool/WB_LOGO.qrc create mode 100644 gender_indicator_tool/__init__.py create mode 100644 gender_indicator_tool/__pycache__/__init__.cpython-39.pyc create mode 100644 gender_indicator_tool/__pycache__/gender_indicator_tool.cpython-39.pyc create mode 100644 gender_indicator_tool/__pycache__/gender_indicator_tool_dialog.cpython-39.pyc create mode 100644 gender_indicator_tool/__pycache__/resources.cpython-39.pyc create mode 100644 gender_indicator_tool/gender_indicator_tool.py create mode 100644 gender_indicator_tool/gender_indicator_tool_dialog.py create mode 100644 gender_indicator_tool/gender_indicator_tool_dialog_base.ui create mode 100644 gender_indicator_tool/icon.png create mode 100644 gender_indicator_tool/metadata.txt create mode 100644 gender_indicator_tool/plugin_upload.py create mode 100644 gender_indicator_tool/pylintrc create mode 100644 gender_indicator_tool/resources.py create mode 100644 gender_indicator_tool/resources.qrc diff --git a/gender_indicator_tool/README.html b/gender_indicator_tool/README.html new file mode 100644 index 0000000..a71afeb --- /dev/null +++ b/gender_indicator_tool/README.html @@ -0,0 +1,42 @@ + + +

Plugin Builder Results

+ +Congratulations! You just built a plugin for QGIS!

+ +
+Your plugin GenderIndicatorTool was created in:
+  C:/Users/Andre/Nextcloud/GIS_WBGIT/WBGIT_Plugin\gender_indicator_tool +

+Your QGIS plugin directory is located at:
+  C:/Users/Andre/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins +

+

What's Next

+
    +
  1. If resources.py is not present in your plugin directory, compile the resources file using pyrcc5 (simply use pb_tool or make if you have automake) +
  2. Optionally, test the generated sources using make test (or run tests from your IDE) +
  3. Copy the entire directory containing your new plugin to the QGIS plugin directory (see Notes below) +
  4. Test the plugin by enabling it in the QGIS plugin manager +
  5. Customize it by editing the implementation file gender_indicator_tool.py +
  6. Create your own custom icon, replacing the default icon.png +
  7. Modify your user interface by opening gender_indicator_tool_dialog_base.ui in Qt Designer +
+Notes: + +
+
+

+For information on writing PyQGIS code, see http://loc8.cc/pyqgis_resources for a list of resources. +

+
+

+©2011-2019 GeoApt LLC - geoapt.com +

+ + diff --git a/gender_indicator_tool/README.txt b/gender_indicator_tool/README.txt new file mode 100644 index 0000000..8cb7796 --- /dev/null +++ b/gender_indicator_tool/README.txt @@ -0,0 +1,32 @@ +Plugin Builder Results + +Your plugin GenderIndicatorTool was created in: + C:/Users/Andre/Nextcloud/GIS_WBGIT/WBGIT_Plugin\gender_indicator_tool + +Your QGIS plugin directory is located at: + C:/Users/Andre/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins + +What's Next: + + * Copy the entire directory containing your new plugin to the QGIS plugin + directory + + * Compile the resources file using pyrcc5 + + * Run the tests (``make test``) + + * Test the plugin by enabling it in the QGIS plugin manager + + * Customize it by editing the implementation file: ``gender_indicator_tool.py`` + + * Create your own custom icon, replacing the default icon.png + + * Modify your user interface by opening GenderIndicatorTool_dialog_base.ui in Qt Designer + + * You can use the Makefile to compile your Ui and resource files when + you make changes. This requires GNU make (gmake) + +For more information, see the PyQGIS Developer Cookbook at: +http://www.qgis.org/pyqgis-cookbook/index.html + +(C) 2011-2018 GeoApt LLC - geoapt.com diff --git a/gender_indicator_tool/Style/Accessibility.qml b/gender_indicator_tool/Style/Accessibility.qml new file mode 100644 index 0000000..f352e1d --- /dev/null +++ b/gender_indicator_tool/Style/Accessibility.qml @@ -0,0 +1,426 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MinMax + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Contextual.qml b/gender_indicator_tool/Style/Contextual.qml new file mode 100644 index 0000000..51226aa --- /dev/null +++ b/gender_indicator_tool/Style/Contextual.qml @@ -0,0 +1,426 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MinMax + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Education.qml b/gender_indicator_tool/Style/Education.qml new file mode 100644 index 0000000..f352e1d --- /dev/null +++ b/gender_indicator_tool/Style/Education.qml @@ -0,0 +1,426 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MinMax + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Final.qml b/gender_indicator_tool/Style/Final.qml new file mode 100644 index 0000000..1de9ef2 --- /dev/null +++ b/gender_indicator_tool/Style/Final.qml @@ -0,0 +1,176 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Individual.qml b/gender_indicator_tool/Style/Individual.qml new file mode 100644 index 0000000..868c13f --- /dev/null +++ b/gender_indicator_tool/Style/Individual.qml @@ -0,0 +1,426 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MinMax + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Insights Aggregation.qml b/gender_indicator_tool/Style/Insights Aggregation.qml new file mode 100644 index 0000000..86c8c0b --- /dev/null +++ b/gender_indicator_tool/Style/Insights Aggregation.qml @@ -0,0 +1,940 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Name" + + 2 + diff --git a/gender_indicator_tool/Style/Insights Buffer Aggregation.qml b/gender_indicator_tool/Style/Insights Buffer Aggregation.qml new file mode 100644 index 0000000..79a019d --- /dev/null +++ b/gender_indicator_tool/Style/Insights Buffer Aggregation.qml @@ -0,0 +1,917 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0.751 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "name" + + 2 + diff --git a/gender_indicator_tool/Style/Insights Combined.qml b/gender_indicator_tool/Style/Insights Combined.qml new file mode 100644 index 0000000..5b79f8b --- /dev/null +++ b/gender_indicator_tool/Style/Insights Combined.qml @@ -0,0 +1,171 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Insights Population.qml b/gender_indicator_tool/Style/Insights Population.qml new file mode 100644 index 0000000..213dcf5 --- /dev/null +++ b/gender_indicator_tool/Style/Insights Population.qml @@ -0,0 +1,159 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Insights Score.qml b/gender_indicator_tool/Style/Insights Score.qml new file mode 100644 index 0000000..d15f4b0 --- /dev/null +++ b/gender_indicator_tool/Style/Insights Score.qml @@ -0,0 +1,162 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/Style/Place Characterization.qml b/gender_indicator_tool/Style/Place Characterization.qml new file mode 100644 index 0000000..04fb6e2 --- /dev/null +++ b/gender_indicator_tool/Style/Place Characterization.qml @@ -0,0 +1,426 @@ + + + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MinMax + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/gender_indicator_tool/WB_LOGO.qrc b/gender_indicator_tool/WB_LOGO.qrc new file mode 100644 index 0000000..94d8291 --- /dev/null +++ b/gender_indicator_tool/WB_LOGO.qrc @@ -0,0 +1,5 @@ + + + WBG_Vertical-RGB-high-resize.png + + diff --git a/gender_indicator_tool/__init__.py b/gender_indicator_tool/__init__.py new file mode 100644 index 0000000..a171050 --- /dev/null +++ b/gender_indicator_tool/__init__.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + GenderIndicatorTool + A QGIS plugin + Gender Indicator Tool + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2023-07-15 + copyright : (C) 2023 by Pegasys + email : andre@pegasys.co.za + git sha : $Format:%H$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + This script initializes the plugin, making it known to QGIS. +""" + + +# noinspection PyPep8Naming +def classFactory(iface): # pylint: disable=invalid-name + """Load GenderIndicatorTool class from file GenderIndicatorTool. + + :param iface: A QGIS interface instance. + :type iface: QgsInterface + """ + # + from .gender_indicator_tool import GenderIndicatorTool + + return GenderIndicatorTool(iface) diff --git a/gender_indicator_tool/__pycache__/__init__.cpython-39.pyc b/gender_indicator_tool/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f816e54123dacecc7ce8e1a09eda70844250fc19 GIT binary patch literal 1784 zcmb_d&ui2`6rS{lEujTLy@-c*sL)-yiMEK4inLa?7D{bxdnpTHHknO^CYdlZv82?K zp8Y>O``5TvPyP#@d^7ultu9rt4>HNjeDlqFZ{B>N%gZi7TO?oOw|5BnZaR}TgPkpW z)Lm?1azMsO;Z?;m+%JGs>oV~Dq@_|Ob6R)_*J;)DxbTk>u37_p zYl!!6_Nn!!jTdwo`uKZ50jxh+-)KE~-g^3MjEqECOP-{95*@(mPQ#*Y6fB{tR^ufs zr#zd4o9_T<5lgmJ4!~X{yh}P>f^mXM>Geecth5EDK?jfDthjJ{2i)}CTP~R9bBtjG)wt8T_4*yj-|6a$L;n-MHUTdN~7!_hY091p#L zwm^2OxhOV)VZq`?@0nEChxO4$L)JAw?~DySTcz5>B0-d9eFFoiA;ar2rtmEM=4MEC zGw5Gu4cYA(-cfs$Dw4d^zzeQ9&G;o#Hva>$Sc9CN^CB@PcwUH$0<^GV;<=H7qBk<5 zgyz}7N8QK9C}Bo&=Y!*rE9Z)QbWjmzFF)@K8c!23h%%~7F^L>{JY&-^o@;MqP*PiZ zJsPn9hT6spJQ*{BfhbLjheNkdNdx z)Tp=81d6lsmeuyY5IK*7KhJl9&Z)u*^S6zp_}gXqnriC56Ew#x`bLIrv(dpDWa3nv znDuCtX}_#>Dhhv)ch#A?btgn$xDG?FtoutgWo;Vaf1rMZ+bjIt5VEl3$OU{Kpog(j?SOT0qQO5J=`X&(8(C%Sr3p%zX71rHh&-?% literal 0 HcmV?d00001 diff --git a/gender_indicator_tool/__pycache__/gender_indicator_tool.cpython-39.pyc b/gender_indicator_tool/__pycache__/gender_indicator_tool.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0feb4a82e3b3a465323b92baba655076154c49bd GIT binary patch literal 93811 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 JA!20W{|9y}b+!Ni literal 0 HcmV?d00001 diff --git a/gender_indicator_tool/__pycache__/gender_indicator_tool_dialog.cpython-39.pyc b/gender_indicator_tool/__pycache__/gender_indicator_tool_dialog.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cb1906dd7fe20aa51b9a3bfbb3cace233453378 GIT binary patch literal 2273 zcmb_d-EQ1O6t=zIKWCSuEl{{YBNRk8uy#pRA#D+ANR?2jglv-t$%G=9wE8xv+xGKMO^R-+`yUHABEB%gmP9hbI$nqoAaGHK8w3u&jN9O{gnRR zvaH{dUxcl$`6-}s_$N|wQ?12k5u7z9Dumt!g>IQLVg#^uOoJQ$`-_GkY6 zxqtBHnEL;gkqf$`>HwB3cxIhz5 zSTaRu4h9cOUDimADN;~b67j?syL(5vwA@mQ&ChkB~@i{#ClHUN}45AqkwA9)eFc> z6vY{qGKyYUfBw8ZI6jh4V!@Vf6v0+eJirPEk2#*OJPmXoHq)JB_=FP3qC6%v!DXg` zVyebG4`y9j9=|fJuil*}+lMCChPXe2Ax(mvwZ`ekug#smpx3kaAj!Zs#}f){y(oh3 zw9K^LiJ~VZ&Sp;xeAS&Zj%k)fkywS}W>Z&dYkJb@+kheUjkIiI_ik3rC?Y%tRENu| zol;&vTfnlar3I;4^D5-wwT0#<6suBDDKt)XeYvj$&Se6PUg+zt+E3s=$lsq1mC(FJ zU2m>x`>ww%T+z_GUdAywVu#a$R&IgSxN=W8%c~|~LX*^K(D$O#u=D-n@1pHTTL%Y~ u4K*;FRlFf{iM8RcSy!#EW@-CcM=L*o$-UXK^{-=h92j<|?%AEcJoj%!4>xB3 literal 0 HcmV?d00001 diff --git a/gender_indicator_tool/__pycache__/resources.cpython-39.pyc b/gender_indicator_tool/__pycache__/resources.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..815f9a431b57889a7cc7ee7ce21f5d47c9af2ae8 GIT binary patch literal 2291 zcmb7FdstIP7N4633E>%#Vgb=Y0Z{>kf~>Nk;T=TeQ7Fc^A>>|S){w`&Hz*1gc~!v` zmx5g@@^lMGtAJ>+w3P)BZI!YrD4`38ekckQODh#9+qozf_w%1U-*zvbtwO0evibT@ z#M2}4FfZ#b8D2+SRvu#qte;9SbqQ+L-yo3-dm04aRgQ5k-spI8d!~8kch&m}hKnb^ zo+=^m@ub=n`m?N|&rCk-d^(hoLaQ{L==#a#(5Zs@HG!GkBl^{yq3@n9yWjl4ykpF# zzxuX)eJZQCVtGvVg}CS=`uToFe+xZm$Y*+kn4j%t; z|HiOqo~2PSZEw2w<8|joqa7dBh2N~r5eR4Uj(=wBd(&kcr2NJo4)$<*)?;Ed61UT- z`*EKja@MNbOFLmvk?{5Y4cEhmElbXNG`9~rez-J#t}fbj$@Su6r6Z62)iS|8J-gvR z{D3C4Z|}01KG%Cy<$YIfnfAyn8`7i+>Ak}pOq-^+T2t4rkZ1W;(aIBhg$L9>o!O$b z=bPsbur@!OK6P0BfoFHC=U6+jR~T{h1Wg>l`^Tq8W!8nfj3DI1)w}}I!Ut(hk9M`O zE1ngd;U-Te{amaZ%neHnb?5!?URM0t14E8SUMZ<5 zYq4l1+oq0G`4@`k9Mf*?&Ism4OnXh)6>9U%nD&-;5-e&W_Z%+>DavVXSjin1nV-2l zWjT}bcIA&vhnB@S_~Z@}e_F@0w#vOW?C``p=}AZlExO|UHym28PJS$t+)=bOmp*Kw zjq-4_<+s0TsbqT`UiWs;pPAZXn*B$AiCw$e^>Q{Zx{HF)f)aRpXUkw~RS!6wNLg^OXD84k)=atai*4rd4z0|wu zx3&F^tvebdt@qmPnJHJUe%sQMYiBby_vQoEnx^E3E+yG*(--q^wO&7YZQB;h=|iG} z5mVUe6Qhsra{q0+eIxcwTu0@FOJTMNw`F_$f}EEr|LXLG?33CX`-{(A^EywO#wt(l zGWI&_{lN(yry+;v&eBBjVI2$g}lJN=;&OCZQVD%ftAhL9i?$wdB6(=ehz4RzJh zj75*DUF$9JaZhR$*V{Ux4IzU27+@e&HB)L-N78xvvo(B(+ z8doLC6&N8xu|$bhLAt3^$Q>%Bn_8ij$(4jihAB}D7t58XTq+?|xR_L_6e4)NtF*Wj zBV5%fI*yM*PLNVnlA5Q56eghpoDD&TxH{8Es4IMgGI6TNk$Ip>ltO()sQ2Jy%_Jy8 zNE%5e86>lu0pAz^YevGI^>8XyXQ7G0WbjlBk9t9cDo|&DOQqsuSh!rJ)R}2W@d8mX zLE>5|sWV;dQ%aJsS3M|9@<$(^&sfxX#u|ZAPA;59q(L7s|D_S!0?z;cT5uB>wor|w zCPs 1: + import debugpy # pylint: disable=import-outside-toplevel + + debugpy.listen(("0.0.0.0", 9000)) + debugpy.wait_for_client() + # self.display_information_message_bar( + # title="Animation Workbench", + # message="Visual Studio Code debugger is now attached on port 9000", + # ) + + # initialize locale + locale = QSettings().value("locale/userLocale")[0:2] + locale_path = os.path.join( + self.plugin_dir, "i18n", "GenderIndicatorTool_{}.qm".format(locale) + ) + + if os.path.exists(locale_path): + self.translator = QTranslator() + self.translator.load(locale_path) + QCoreApplication.installTranslator(self.translator) + + # Declare instance attributes + self.actions = [] + self.menu = self.tr("&Gender Enabling Environments Spatial Tool (GEEST)") + + # Check if plugin was started the first time in current QGIS session + # Must be set in initGui() to survive plugin reloads + self.first_start = None + + # Add a global buffer distance constant + # This value used to be 2000 + self.BUFFER_DISTANCE = 0 + + # noinspection PyMethodMayBeStatic + def tr(self, message): + """Get the translation for a string using Qt translation API. + + We implement this ourselves since we do not inherit QObject. + + :param message: String for translation. + :type message: str, QString + + :returns: Translated version of message. + :rtype: QString + """ + # noinspection PyTypeChecker,PyArgumentList,PyCallByClass + return QCoreApplication.translate("GenderIndicatorTool", message) + + def add_action( + self, + icon_path, + text, + callback, + enabled_flag=True, + add_to_menu=True, + add_to_toolbar=True, + status_tip=None, + whats_this=None, + parent=None, + ): + """Add a toolbar icon to the toolbar. + + :param icon_path: Path to the icon for this action. Can be a resource + path (e.g. ':/plugins/foo/bar.png') or a normal file system path. + :type icon_path: str + + :param text: Text that should be shown in menu items for this action. + :type text: str + + :param callback: Function to be called when the action is triggered. + :type callback: function + + :param enabled_flag: A flag indicating if the action should be enabled + by default. Defaults to True. + :type enabled_flag: bool + + :param add_to_menu: Flag indicating whether the action should also + be added to the menu. Defaults to True. + :type add_to_menu: bool + + :param add_to_toolbar: Flag indicating whether the action should also + be added to the toolbar. Defaults to True. + :type add_to_toolbar: bool + + :param status_tip: Optional text to show in a popup when mouse pointer + hovers over the action. + :type status_tip: str + + :param parent: Parent widget for the new action. Defaults None. + :type parent: QWidget + + :param whats_this: Optional text to show in the status bar when the + mouse pointer hovers over the action. + + :returns: The action that was created. Note that the action is also + added to self.actions list. + :rtype: QAction + """ + + icon = QIcon(icon_path) + action = QAction(icon, text, parent) + action.triggered.connect(callback) + action.setEnabled(enabled_flag) + + if status_tip is not None: + action.setStatusTip(status_tip) + + if whats_this is not None: + action.setWhatsThis(whats_this) + + if add_to_toolbar: + # Adds plugin icon to Plugins toolbar + self.iface.addToolBarIcon(action) + + if add_to_menu: + self.iface.addPluginToMenu(self.menu, action) + + self.actions.append(action) + + return action + + def initGui(self): + """Create the menu entries and toolbar icons inside the QGIS GUI.""" + + icon_path = ":/plugins/gender_indicator_tool/icon.png" + self.add_action( + icon_path, + text=self.tr("GEEST"), + callback=self.run, + parent=self.iface.mainWindow(), + ) + + # will be set False in run() + self.first_start = True + + def unload(self): + """Removes the plugin menu item and icon from QGIS GUI.""" + for action in self.actions: + self.iface.removePluginMenu( + self.tr("&World Bank Gender Indicator Tool"), action + ) + self.iface.removeToolBarIcon(action) + + def run(self): + """Run method that performs all the real work""" + + # Create the dialog with elements (after translation) and keep reference + # Only create GUI ONCE in callback, so that it will only load when the plugin is started + if self.first_start == True: + self.first_start = False + self.dlg = GenderIndicatorToolDialog() + + # show the dialog + self.dlg.show() + + ## TAB 1 - Analysis Setup *********************************************************************** + self.dlg.workingDir_Button.clicked.connect(lambda: self.getFolder(0)) + + ## TAB 2 - Contextual *************************************************************************** + + ###### TAB 2.1 - Workplace Discrimination + self.dlg.WD_Execute_PB.clicked.connect(lambda: self.Rasterize(1)) + + ###### TAB 2.2 - Regulatory Frameworks + self.dlg.RF_Execute_PB.clicked.connect(lambda: self.Rasterize(2)) + + ###### TAB 2.3 - Financial Inclusion + self.dlg.FIN_Execute_PB.clicked.connect(lambda: self.Rasterize(3)) + + ###### TAB 2.2 - Aggregate + self.dlg.WD_Aggregate_TB.clicked.connect(lambda: self.getFile(1)) + self.dlg.RF_Aggregate_TB.clicked.connect(lambda: self.getFile(3)) + self.dlg.FIN_Aggregate_TB.clicked.connect(lambda: self.getFile(4)) + + self.dlg.Contextual_AggregateExecute_PB.clicked.connect( + self.contextualAggregation + ) + + ## TAB 3 - Accessibility ************************************************************************ + Modes = ["Walking", "Driving"] + Measurement = ["Distance", "Time"] + + ###### TAB 3.1 - Women's Travel Patterns + self.dlg.WTP_mode_CB.clear() + self.dlg.WTP_mode_CB.addItems(Modes) + self.dlg.WTP_measurement_CB.clear() + self.dlg.WTP_measurement_CB.addItems(Measurement) + self.dlg.WTP_Execute_PB.clicked.connect(lambda: self.ServiceArea(5)) + + self.dlg.WTP_Aggregate_PB.clicked.connect(self.wtpAggregate) + + ###### TAB 3.2 - Public Transport + self.dlg.PBT_mode_CB.clear() + self.dlg.PBT_mode_CB.addItems(Modes) + self.dlg.PBT_measurement_CB.clear() + self.dlg.PBT_measurement_CB.addItems(Measurement) + self.dlg.PBT_Execute_PB.clicked.connect(lambda: self.ServiceArea(0)) + + ###### TAB 3.3 - Education & Training + self.dlg.ETF_mode_CB.clear() + self.dlg.ETF_mode_CB.addItems(Modes) + self.dlg.ETF_measurement_CB.clear() + self.dlg.ETF_measurement_CB.addItems(Measurement) + self.dlg.ETF_Execute_PB.clicked.connect(lambda: self.ServiceArea(1)) + + ###### TAB 3.4 - Health Facilities + self.dlg.HEA_mode_CB.clear() + self.dlg.HEA_mode_CB.addItems(Modes) + self.dlg.HEA_measurement_CB.clear() + self.dlg.HEA_measurement_CB.addItems(Measurement) + self.dlg.HEA_Execute_PB.clicked.connect(lambda: self.ServiceArea(3)) + + ###### TAB 3.5 - Financial Facilities + self.dlg.FIF_mode_CB.clear() + self.dlg.FIF_mode_CB.addItems(Modes) + self.dlg.FIF_measurement_CB.clear() + self.dlg.FIF_measurement_CB.addItems(Measurement) + self.dlg.FIF_Execute_PB.clicked.connect(lambda: self.ServiceArea(4)) + + ###### TAB 3.6 - Aggregate + self.dlg.WTP_Aggregate_TB.clicked.connect(lambda: self.getFile(5)) + self.dlg.PBT_Aggregate_TB.clicked.connect(lambda: self.getFile(6)) + self.dlg.ETF_Aggregate_TB.clicked.connect(lambda: self.getFile(7)) + self.dlg.HEA_Aggregate_TB.clicked.connect(lambda: self.getFile(9)) + self.dlg.FIF_Aggregate_TB.clicked.connect(lambda: self.getFile(10)) + self.dlg.Accessibility_AggregateExecute_PB.clicked.connect( + self.accessibiltyAggregation + ) + + ## TAB 4 - Place Charqacterization ************************************************************** + ###### TAB 4.1 - Walkability / Active Transport + #self.dlg.WLK_Set_PB.clicked.connect(lambda: self.TypeSet(1)) + #self.dlg.WLK_unique_PB.clicked.connect(lambda: self.uniqueValues(1)) + self.dlg.WLK_Execute_PB_2.clicked.connect(self.walkability) + self.dlg.AT_Aggregate_PB.clicked.connect(self.walkabilityAggregate) + + ###### TAB 4.2 - Safe Urban Design + self.dlg.SAF_Input_Field.fileChanged.connect(self.populateCBFieldsFromPolygonLayer_PC_SAF) + self.dlg.SAF_rasField_CB.textActivated.connect(self.populateTextFieldWithUniqueValues_PC_SAF) + self.dlg.SAF_Execute_PB.clicked.connect(self.SAFRasterizerDelegator) + + ###### TAB 4.3 - Digital Inclusion + self.dlg.DIG_Set_PB.clicked.connect(lambda: self.RasterizeSet(4)) + self.dlg.DIG_Execute_PB.clicked.connect(lambda: self.Rasterize(5)) + + ###### TAB 4.4 - Natural Environment + #self.dlg.ENV_Set_PB.clicked.connect(lambda: self.TypeSet(3)) + #self.dlg.ENV_Input_Field.fileChanged.connect(self.populateCBFieldsFromPolygonLayer_PC_ENV) + #self.dlg.ENV_unique_PB.clicked.connect(lambda: self.uniqueValues(3)) + #self.dlg.ENV_riskLevelField_CB.textActivated.connect(self.populateTextFieldWithUniqueValues_PC_ENV) + self.dlg.ENV_Execute_PB.clicked.connect(self.natEnvironment) + self.dlg.ENV_Aggregate_PB.clicked.connect(self.envAggregate) + + ###### TAB 4.5- Education + #self.dlg.EDU_Set_PB.clicked.connect(lambda: self.RasterizeSet(0)) + self.dlg.EDU_Input_Field.fileChanged.connect(self.populateCBFieldsFromPolygonLayer_PC_EDU) + self.dlg.EDU_Execute_PB.clicked.connect(self.EDUShapefileOrUserInputRasterizer) + + ###### TAB 4.6 - Facility, conflict, and violence + #self.dlg.FCV_Set_PB.clicked.connect(lambda: self.RasterizeSet(5)) + self.dlg.FCV_Execute_PB.clicked.connect(lambda: self.Rasterize(6)) + + ###### TAB 4.7 - Water and Sanitation + self.dlg.WAS_Execute_PB.clicked.connect(lambda: self.Rasterize(7)) + + ###### TAB 4.7 - Aggregate + self.dlg.WLK_Aggregate_TB.clicked.connect(lambda: self.getFile(11)) + self.dlg.SAF_Aggregate_TB.clicked.connect(lambda: self.getFile(14)) + self.dlg.DIG_Aggregate_TB.clicked.connect(lambda: self.getFile(20)) + self.dlg.ENV_Aggregate_TB.clicked.connect(lambda: self.getFile(21)) + self.dlg.EDU_Aggregate_TB.clicked.connect(lambda: self.getFile(0)) + self.dlg.FCV_Aggregate_TB.clicked.connect(lambda: self.getFile(26)) + self.dlg.WAS_Aggregate_TB.clicked.connect(lambda: self.getFile(27)) + self.dlg.PlaceCharacterization_AggregateExecute_PB.clicked.connect( + self.placeCharacterizationAggregation + ) + + ## TAB 5 - Dimension Aggregation ************************************************************************ + #self.dlg.ID_Aggregate_TB.clicked.connect(lambda: self.getFile(22)) + self.dlg.CD_Aggregate_TB.clicked.connect(lambda: self.getFile(23)) + self.dlg.AD_Aggregate_TB.clicked.connect(lambda: self.getFile(24)) + self.dlg.PD_Aggregate_TB.clicked.connect(lambda: self.getFile(25)) + self.dlg.Dimensions_AggregateExecute_PB.clicked.connect( + self.dimesnionsAggregation + ) + + ## TAB 6 - Insights ************************************************************************ + ###### TAB 6.1 - Enablement + self.dlg.Score_reclassify.clicked.connect(self.scoreReclassInsights) + self.dlg.Pop_reclassify.clicked.connect(self.populationReclassInsights) + self.dlg.Combine_reclassify.clicked.connect(self.combineReclassInsights) + self.dlg.Aggregation_Execute_PB.clicked.connect(self.Aggregationinsights) + + ###### TAB 6.2 - Raster Locations + self.dlg.RE_Execute_PB.clicked.connect(self.reZones) + + ###### TAB 6.3 - Point Locations + self.dlg.Buffer_Execute_PB.clicked.connect(self.Bufferinsights) + + # ******************************************************************************************* Weights Auto-calc # + # weights auto-calc button + self.dlg.PB_PC_aggregate_auto.clicked.connect(lambda: self.calculate_aggregate_weights(self.PC_aggregate_field_pairs)) + self.dlg.PB_CTX_aggregate_auto.clicked.connect(lambda: self.calculate_aggregate_weights(self.CTX_aggregate_field_pairs)) + self.dlg.PB_ACC_aggregate_auto.clicked.connect(lambda: self.calculate_aggregate_weights(self.ACC_aggregate_field_pairs)) + self.dlg.PB_AGG_aggregate_auto.clicked.connect(lambda: self.calculate_aggregate_weights(self.AGG_aggregate_field_pairs)) + + # Place Characterization field pairs + self.PC_aggregate_field_pairs = [ + (self.dlg.AT_Aggregate_Field, self.dlg.WLK_Aggregate_SB), + (self.dlg.SAF_Aggregate_Field, self.dlg.SAF_Aggregate_SB), + (self.dlg.EDU_Aggregate_Field, self.dlg.EDU_Aggregate_SB), + (self.dlg.DIG_Aggregate_Field, self.dlg.DIG_Aggregate_SB), + (self.dlg.ENV_Aggregate_Field, self.dlg.ENV_Aggregate_SB), + (self.dlg.FCV_Aggregate_Field, self.dlg.FCV_Aggregate_SB), + (self.dlg.WAS_Aggregate_Field, self.dlg.WAS_Aggregate_SB), + ] + + self.CTX_aggregate_field_pairs = [ + (self.dlg.WD_Aggregate_Field, self.dlg.WD_Aggregate_SB), + (self.dlg.RF_Aggregate_Field, self.dlg.RF_Aggregate_SB), + (self.dlg.FIN_Aggregate_Field, self.dlg.FIN_Aggregate_SB) + ] + + self.ACC_aggregate_field_pairs = [ + (self.dlg.WTP_Aggregate_Field, self.dlg.WTP_Aggregate_SB), + (self.dlg.PBT_Aggregate_Field, self.dlg.PBT_Aggregate_SB), + (self.dlg.ETF_Aggregate_Field, self.dlg.ETF_Aggregate_SB), + (self.dlg.HEA_Aggregate_Field, self.dlg.HEA_Aggregate_SB), + (self.dlg.FIF_Aggregate_Field, self.dlg.FIF_Aggregate_SB) + ] + + self.AGG_aggregate_field_pairs = [ + (self.dlg.CD_Aggregate_Field, self.dlg.CD_Aggregate_SB), + (self.dlg.AD_Aggregate_Field, self.dlg.AD_Aggregate_SB), + (self.dlg.PD_Aggregate_Field, self.dlg.PD_Aggregate_SB) + ] + + # ================================================================================================================ # + + def calculate_aggregate_weights(self, field_pairs): + non_empty_fields = [pair for pair in field_pairs if pair[0].text()] + num_non_empty = len(non_empty_fields) + + if num_non_empty == 0: + return # No fields with text, nothing to do + + weight_per_field = 100 / num_non_empty + + for line_edit, spin_box in field_pairs: + if line_edit.text(): + spin_box.setValue(weight_per_field) + else: + spin_box.setValue(0) + + # Ensure total is exactly 100 + total = sum(pair[1].value() for pair in field_pairs) + if total != 100: + # Adjust the last non-empty field to make the total exactly 100 + last_non_empty = next((pair[1] for pair in reversed(field_pairs) if pair[0].text()), None) + if last_non_empty: + last_non_empty.setValue(last_non_empty.value() + (100 - total)) + + + def getFile(self, button_num): + """ + Function used for all factor tabs to browse files and retrieve filepaths required to be used as input to other functions. + + Factors it is applied: + - All + """ + response = QFileDialog.getOpenFileName( + parent=self.dlg, caption="Select a file", directory=os.getcwd() + ) + + if button_num == 0: + self.dlg.EDU_Aggregate_Field.setText(response[0]) + + elif button_num == 1: + self.dlg.WD_Aggregate_Field.setText(response[0]) + + elif button_num == 3: + self.dlg.RF_Aggregate_Field.setText(response[0]) + + elif button_num == 4: + self.dlg.FIN_Aggregate_Field.setText(response[0]) + + elif button_num == 5: + self.dlg.WTP_Aggregate_Field.setText(response[0]) + + elif button_num == 6: + self.dlg.PBT_Aggregate_Field.setText(response[0]) + + elif button_num == 7: + self.dlg.ETF_Aggregate_Field.setText(response[0]) + + elif button_num == 9: + self.dlg.HEA_Aggregate_Field.setText(response[0]) + + elif button_num == 10: + self.dlg.FIF_Aggregate_Field.setText(response[0]) + + elif button_num == 11: + self.dlg.AT_Aggregate_Field.setText(response[0]) + + elif button_num == 12: + self.dlg.CYC_Aggregate_Field.setText(response[0]) + + elif button_num == 14: + self.dlg.SAF_Aggregate_Field.setText(response[0]) + + elif button_num == 20: + self.dlg.DIG_Aggregate_Field.setText(response[0]) + + elif button_num == 21: + self.dlg.ENV_Aggregate_Field.setText(response[0]) + + elif button_num == 22: + self.dlg.ID_Aggregate_Field.setText(response[0]) + + elif button_num == 23: + self.dlg.CD_Aggregate_Field.setText(response[0]) + + elif button_num == 24: + self.dlg.AD_Aggregate_Field.setText(response[0]) + + elif button_num == 25: + self.dlg.PD_Aggregate_Field.setText(response[0]) + + elif button_num == 26: + self.dlg.FCV_Aggregate_Field.setText(response[0]) + + elif button_num == 27: + self.dlg.WAS_Aggregate_Field.setText(response[0]) + + def getFolder(self, button_num): + """ + Function used to browse and retrieve filepath for project working directory selected in the setup tab + """ + response = QFileDialog.getExistingDirectory( + parent=self.dlg, caption="Select a folder/directory", directory=os.getcwd() + ) + + if button_num == 0: + # Clear the field before setting its text + self.dlg.workingDir_Field.clear() + self.dlg.workingDir_Field.setText(str(response + "/")) + + def RasterizeSet(self, factor_no): + """ + Used in combination with tabs that use the "Rasterization" function. This function is used to extract all the attribute table's field headings + from the input vector layers. The extracted values are then populated in the drop-down menu allowing the user to select which fields values are to be used when + executing the "Rasterization" function. + + Factors it is applied: + Contextual Dimension + - Workplace Discrimination + - Regulatory Frameworks(RF) + - Financial Inclusion + Place Characterization Dimension + - Education + - Safety + - Income Level + - Digital Inclusion + - Fragility, conflict, and violence(FCV) + """ + if factor_no == 0: + """polygonlayer = self.dlg.EDU_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + self.dlg.EDU_rasField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.EDU_rasField_CB.addItems(fields)""" + + elif factor_no == 1: + polygonlayer = self.dlg.WD_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + self.dlg.WD_rasField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.WD_rasField_CB.addItems(fields) + + elif factor_no == 2: + polygonlayer = self.dlg.RF_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + self.dlg.RF_rasField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.RF_rasField_CB.addItems(fields) + + elif factor_no == 3: + polygonlayer = self.dlg.FIN_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + self.dlg.FIN_rasField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.FIN_rasField_CB.addItems(fields) + + elif factor_no == 4: + polygonlayer = self.dlg.DIG_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + self.dlg.DIG_rasField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.DIG_rasField_CB.addItems(fields) + + elif factor_no == 5: + polygonlayer = self.dlg.FCV_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + #self.dlg.FCV_rasField_CB.clear() + #fields = [field.name() for field in layer.fields()] + #self.dlg.FCV_rasField_CB.addItems(fields) + + def TypeSet(self, factor_no): + """ + Similar to the "RasterizeSet" function this function is used to extract all the attribute table's field headings from the input vector layers. However, + the extracted values are then populated in the drop-down menu where the user is to select which field from which they want extract the unique values. (See uniqueValues function) + + Factors it is applied: + Place Characterization Dimension + - Active Transport + - Natural Environment and Climatic Factors + """ + if factor_no == 1: + roadlayer = self.dlg.WLK_Input_Field.filePath() + layer = QgsVectorLayer(roadlayer, "polygonlayer", "ogr") + self.dlg.WLK_roadTypeField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.WLK_roadTypeField_CB.addItems(fields) + + elif factor_no == 2: + roadlayer = self.dlg.CYC_Input_Field.filePath() + layer = QgsVectorLayer(roadlayer, "polygonlayer", "ogr") + self.dlg.CYC_roadTypeField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.CYC_roadTypeField_CB.addItems(fields) + + elif factor_no == 3: + polygonlayer = self.dlg.ENV_Input_Field.filePath() + layer = QgsVectorLayer(polygonlayer, "polygonlayer", "ogr") + self.dlg.ENV_riskLevelField_CB.clear() + fields = [field.name() for field in layer.fields()] + self.dlg.ENV_riskLevelField_CB.addItems(fields) + + def uniqueValues(self, factor_no): + """ + This function is used in combination with the "TypeSet" function. Once the field of interest in the polygon layer has been selected this function + extracts all the unique values in the selected field and populates the empty field with a list of the unique value in the following format: + + [["low", 0], ["medium", 0], ["high", 0]] + + The user than assigns scores according to the standardized scaling system to each unique value as seen below: + + [["low", 4], ["medium", 2], ["high", 1]] + + Factors it is applied: + Place Characterization Dimension + - Active Transport + - Natural Environment and Climatic Factors + """ + if factor_no == 1: + gdf = gpd.read_file(self.dlg.WLK_Input_Field.filePath()) + roadTypeField = self.dlg.WLK_roadTypeField_CB.currentText() + uniqueValues = gdf[roadTypeField].unique().tolist() + scoreList = [] + + for val in uniqueValues: + scoreList.append([val, 0]) + + self.dlg.WLK_typeScore_Field.setText(str(scoreList)) + + elif factor_no == 2: + gdf = gpd.read_file(self.dlg.CYC_Input_Field.filePath()) + roadTypeField = self.dlg.CYC_roadTypeField_CB.currentText() + self.dlg.CYC_roadType_CB.clear() + uniqueValues = gdf[roadTypeField].unique().tolist() + self.dlg.CYC_roadType_CB.addItems(uniqueValues) + + if factor_no == 3: + gdf = gpd.read_file(self.dlg.ENV_Input_Field.filePath()) + riskTypeField = self.dlg.ENV_riskLevelField_CB.currentText() + uniqueValues = gdf[riskTypeField].unique().tolist() + scoreList = [] + + for val in uniqueValues: + scoreList.append([val, 0]) + + self.dlg.ENV_typeScore_Field.setText(str(scoreList)) + + def convertCRS(self, vector, UTM_crs): + """ + This function is used whenever a vector file's co-ordinate reference system needs to be reprojected so that it can be used as input into an algorithm. + + Factors it is applied: + - All + """ + global shp_utm + + shp = gpd.read_file(vector) + shp_wgs84 = shp.to_crs("EPSG:4326") + shp_utm = shp_wgs84.to_crs(UTM_crs) + + # *************************** Factor Functions ********************************** # + def Rasterize(self, factor_no): + """ + Numerous functions use this function to convert vector file type into the standardized raster file types required for aggregation. + + Factors it is applied: + Contextual Dimension + - Workplace Discrimination + - Regulatory Frameworks(RF) + - Financial Inclusion + Place Characterization Dimension + - Education + - Fragility, conflict, and violence(FCV) + - Digital Inclusion + - Water and Sanitation (WAS) + """ + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(":")[-1][:-1] + + # INPUT + if factor_no == 0: + """polygonlayer = self.dlg.EDU_Input_Field.filePath() + rasField = self.dlg.EDU_rasField_CB.currentText() + self.dlg.EDU_status.setText("Variables Set") + self.dlg.EDU_status.repaint() + time.sleep(0.5) + self.dlg.EDU_status.setText("Processing...") + self.dlg.EDU_status.repaint()""" + + elif factor_no == 1: + # polygonlayer = self.dlg.WD_Input_Field.filePath() + rasField = "rasField" + WD_Value_SB = self.dlg.WD_User_Value_Input.value() + self.dlg.WD_status.setText("Variables Set") + self.dlg.WD_status.repaint() + time.sleep(0.5) + self.dlg.WD_status.setText("Processing...") + self.dlg.WD_status.repaint() + + elif factor_no == 2: + # polygonlayer = self.dlg.RF_Input_Field.filePath() + rasField = "rasField" + RF_Value_SB = self.dlg.RF_User_Value_Input.value() + RF_Value_SB_2 = self.dlg.RF_User_Value_Input_2.value() + RF_Value_Average = (RF_Value_SB + RF_Value_SB_2) / 2 + self.dlg.RF_status.setText("Variables Set") + self.dlg.RF_status.repaint() + time.sleep(0.5) + self.dlg.RF_status.setText("Processing...") + self.dlg.RF_status.repaint() + + elif factor_no == 3: + # polygonlayer = self.dlg.FIN_Input_Field.filePath() + rasField = "rasField" + FIN_Value_SB = self.dlg.FIN_User_Value_Input.value() + self.dlg.FIN_status.setText("Variables Set") + self.dlg.FIN_status.repaint() + time.sleep(0.5) + self.dlg.FIN_status.setText("Processing...") + self.dlg.FIN_status.repaint() + + elif factor_no == 4: + polygonlayer = self.dlg.SEC_Input_Field.filePath() + rasField = self.dlg.SEC_rasField_CB.currentText() + self.dlg.SEC_status.setText("Variables Set") + self.dlg.SEC_status.repaint() + time.sleep(0.5) + self.dlg.SEC_status.setText("Processing...") + self.dlg.SEC_status.repaint() + + elif factor_no == 5: + polygonlayer = self.dlg.DIG_Input_Field.filePath() + rasField = self.dlg.DIG_rasField_CB.currentText() + if len(polygonlayer) < 1: + rasField = "rasField" + DIG_Value_SB = self.dlg.DIG_User_Value_Input.value() + self.dlg.DIG_status.setText("Variables Set") + self.dlg.DIG_status.repaint() + time.sleep(0.5) + self.dlg.DIG_status.setText("Processing...") + self.dlg.DIG_status.repaint() + + elif factor_no == 6: + csvFile = self.dlg.FCV_Input_Field.filePath() + csvFile = f"file:///{csvFile.replace(os.sep, '/')}?type=csv&maxFields=10000&detectTypes=yes&xField=longitude&yField=latitude&crs=EPSG:4326&spatialIndex=no&subsetIndex=no&watchFile=no" + rasField = "rasField" + self.dlg.FCV_status.setText("Variables Set") + self.dlg.FCV_status.repaint() + time.sleep(0.5) + self.dlg.FCV_status.setText("Processing...") + self.dlg.FCV_status.repaint() + + elif factor_no == 7: + pointlayer = self.dlg.WAS_Input_Field.filePath() + #rasField = self.dlg.WAS_rasField_CB.currentText() + rasField = "rasField" + self.dlg.WAS_status.setText("Variables Set") + self.dlg.WAS_status.repaint() + time.sleep(0.5) + self.dlg.WAS_status.setText("Processing...") + self.dlg.WAS_status.repaint() + + # Convert countryLayer data to UTM CRS + self.convertCRS(countryLayer, UTM_crs) + shp_utm[rasField] = [0] + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + #outputPathCountry = f"{workingDir}temp/countryBuff.shp" + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + countryUTMLayerBuf = buffer["OUTPUT"] + + # Convert spatial data to UTM CRS + if factor_no in [1, 2, 3, 6]: + pass + elif factor_no in [5]: + if len(polygonlayer) > 1: + self.convertCRS(polygonlayer, UTM_crs) + else: + pass + elif factor_no in [7]: + self.convertCRS(pointlayer, UTM_crs) + else: + self.convertCRS(polygonlayer, UTM_crs) + + if rasField not in shp_utm.columns: + shp_utm[rasField] = [0] * len(shp_utm) + + shp_utm[rasField] = shp_utm[rasField].astype(float) + + # Set variables required to conduct standardization of values + Rmax = 100 + Rmin = 0 + m_max = 5 + m_min = 0 + if factor_no == 0: + # Standardization formula + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + polygonUTM = QgsVectorLayer(shp_utm.to_json(), "polygonUTM", "ogr") + + clipPolygonUTM = processing.run( + "native:clip", + { + "INPUT": polygonUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + polygonUTM = clipPolygonUTM["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": polygonUTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [polygonUTM, difference], + "CRS": None, + "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Place Characterization" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.EDU_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.EDU_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.EDU_status.setText("Processing has been completed!") + self.dlg.EDU_status.repaint() + + elif factor_no == 1: + shp_utm[rasField] = WD_Value_SB + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + polygonUTM = QgsVectorLayer(shp_utm.to_json(), "polygonUTM", "ogr") + + clipPolygonUTM = processing.run( + "native:clip", + { + "INPUT": polygonUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + polygonUTM = clipPolygonUTM["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": polygonUTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [polygonUTM, difference], "CRS": None, "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Contextual" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.WD_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.WD_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.WD_status.setText("Processing has been completed!") + self.dlg.WD_status.repaint() + + elif factor_no == 2: + shp_utm[rasField] = RF_Value_Average + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + polygonUTM = QgsVectorLayer(shp_utm.to_json(), "polygonUTM", "ogr") + + clipPolygonUTM = processing.run( + "native:clip", + { + "INPUT": polygonUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + polygonUTM = clipPolygonUTM["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": polygonUTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [polygonUTM, difference], "CRS": None, "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Contextual" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.RF_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.RF_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.RF_status.setText("Processing has been completed!") + self.dlg.RF_status.repaint() + + elif factor_no == 3: + shp_utm[rasField] = FIN_Value_SB + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + polygonUTM = QgsVectorLayer(shp_utm.to_json(), "polygonUTM", "ogr") + + clipPolygonUTM = processing.run( + "native:clip", + { + "INPUT": polygonUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + polygonUTM = clipPolygonUTM["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": polygonUTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [polygonUTM, difference], "CRS": None, "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Contextual" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.FIN_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.FIN_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.FIN_status.setText("Processing has been completed!") + self.dlg.FIN_status.repaint() + + elif factor_no == 4: + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + polygonUTM = QgsVectorLayer(shp_utm.to_json(), "polygonUTM", "ogr") + + clipPolygonUTM = processing.run( + "native:clip", + { + "INPUT": polygonUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + polygonUTM = clipPolygonUTM["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": polygonUTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [polygonUTM, difference], "CRS": None, "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Place Characterization" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.INC_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.INC_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.INC_status.setText("Processing has been completed!") + self.dlg.INC_status.repaint() + + + elif factor_no == 5: + if len(polygonlayer) < 1: + shp_utm[rasField] = DIG_Value_SB + else: + shp_utm[rasField] = shp_utm[rasField] + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + polygonUTM = QgsVectorLayer(shp_utm.to_json(), "polygonUTM", "ogr") + + clipPolygonUTM = processing.run( + "native:clip", + { + "INPUT": polygonUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + polygonUTM = clipPolygonUTM["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": polygonUTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [polygonUTM, difference], "CRS": None, "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Place Characterization" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.DIG_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.DIG_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.DIG_status.setText("Processing has been completed!") + self.dlg.DIG_status.repaint() + + elif factor_no == 6: + shp_utm[rasField] = 100 + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + + # csv conversion to shapefile + + temp = "temp" + if os.path.exists(temp): + pass + else: + os.mkdir(temp) + + outputPath = f"{workingDir}{temp}/acled.shp" + crs = QgsCoordinateReferenceSystem('EPSG:4326') + + layer = QgsVectorLayer(csvFile, 'acled', 'delimitedtext') + layer.setCrs(crs) + save_options = QgsVectorFileWriter.SaveVectorOptions() + + save_options.driverName = "ESRI Shapefile" + + save_options.fileEncoding = "UTF-8" + + transform_context = QgsProject.instance().transformContext() + error = QgsVectorFileWriter.writeAsVectorFormatV3( + layer, + outputPath, + transform_context, + save_options + ) + + if error[0] == QgsVectorFileWriter.NoError: + print('Shapefile successfully created!') + else: + print('Error occurred:', error) + + csvFileLayer = outputPath + self.convertCRS(csvFileLayer, UTM_crs) + csvFileLayerUTM = QgsVectorLayer(shp_utm.to_json(), "csvFileLayerUTM", "ogr") + + clipCsvFileUTM = processing.run( + "native:clip", + { + "INPUT": csvFileLayerUTM, + "OVERLAY": countryUTMLayerBuf, + "OUTPUT": "memory:", + } + ) + + csvFileLayerUTM = clipCsvFileUTM["OUTPUT"] + + gridExtent = countryUTMLayerBuf.extent() + grid_params = { + 'TYPE': 2, # Rectangle (polygon) + 'EXTENT': gridExtent, + 'HSPACING': 100, # Horizontal spacing + 'VSPACING': 100, # Vertical spacing + 'CRS': UTM_crs, + 'OUTPUT': "memory:" + } + + grid_result = processing.run('native:creategrid', grid_params) + grid_layer = grid_result['OUTPUT'] + + field_name = 'reclass_va' + if not grid_layer.fields().indexFromName(field_name) >= 0: + grid_layer.dataProvider().addAttributes([QgsField(field_name, QVariant.Int)]) + grid_layer.updateFields() + + radius = self.dlg.FCV_Input_Radius.value() + + if radius > 0: + radius = radius + else: + radius = 5000 + + buffer_result = processing.run( + "native:buffer", + { + "INPUT": csvFileLayerUTM, + "DISTANCE": 5000, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": False, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + buffer_layer = buffer_result["OUTPUT"] + + event_type_scores = { + 'Battle': 0, + 'Explosions': 0, + 'Remote violence': 1, + 'Violence against civilians': 2, + 'Protests': 4, + 'Riots': 4, + 'no_event': 5 + } + + buffer_layer_provider = buffer_layer.dataProvider() + buffer_layer_provider.addAttributes([QgsField("score", QVariant.Int)]) + buffer_layer.updateFields() + + buffer_layer.startEditing() + for feature in buffer_layer.getFeatures(): + event_type = feature['event_type'] + score = event_type_scores.get(event_type, event_type_scores['no_event']) + feature.setAttribute("score", score) + buffer_layer.updateFeature(feature) + + buffer_layer.commitChanges() + + grid_layer_provider = grid_layer.dataProvider() + grid_layer_provider.addAttributes([QgsField("score", QVariant.Int)]) + grid_layer.updateFields() + + + # Create a spatial index for the buffer layer + spatial_index = QgsSpatialIndex(buffer_layer.getFeatures()) + + # Start editing the grid layer + grid_layer.startEditing() + + for grid_feature in grid_layer.getFeatures(): + grid_geom = grid_feature.geometry() + intersecting_ids = spatial_index.intersects(grid_geom.boundingBox()) + + min_score = event_type_scores['no_event'] + + # Check intersections and find the minimum score (most serious event) + for buffer_id in intersecting_ids: + buffer_feature = buffer_layer.getFeature(buffer_id) + buffer_geom = buffer_feature.geometry() + + if grid_geom.intersects(buffer_geom): + buffer_score = buffer_feature['score'] + if buffer_score < min_score: + min_score = buffer_score + + # Set the minimum score (most serious event) to the grid cell + grid_feature.setAttribute('score', min_score) + grid_layer.updateFeature(grid_feature) + grid_layer.commitChanges() + + outputPath1 = f"{workingDir}{temp}/clipBuff.shp" + + clipBuffer = processing.run( + "native:clip", + { + "INPUT": grid_layer, + "OVERLAY": countryUTMLayer, + "OUTPUT": "memory:", + } + ) + + grid_layer = clipBuffer["OUTPUT"] + + outputPath2 = f"{workingDir}{temp}/difference.shp" + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": grid_layer, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + outputPath3 = f"{workingDir}{temp}/merge.shp" + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [difference, grid_layer], "CRS": None, "OUTPUT": "memory:"}, + ) + + grid_layer = Merge["OUTPUT"] + + mergeOutput = grid_layer + + # Get the width and height of the extent + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Place Characterization" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.FCV_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": "score", + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.FCV_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.FCV_status.setText("Processing has been completed!") + self.dlg.FCV_status.repaint() + + elif factor_no == 7: + shp_utm[rasField] = 99 + shp_utm[rasField] = (shp_utm[rasField] - Rmin) / (Rmax - Rmin) * m_max + pointUTM = QgsVectorLayer(shp_utm.to_json(), "pointUTM", "ogr") + # + #shp_utm[rasField] = [0] + + #for i in roadType_Score: + # shp_utm.loc[shp_utm[roadTypeField] == i[0], "Score"] = i[1] + + #shp_utm[rasField] = shp_utm[rasField].astype(int) + gridExtent = countryUTMLayerBuf.extent() + grid_params = { + 'TYPE': 2, # Rectangle (polygon) + 'EXTENT': gridExtent, + 'HSPACING': 100, # Horizontal spacing + 'VSPACING': 100, # Vertical spacing + 'CRS': UTM_crs, + 'OUTPUT': "memory:" + } + + grid_result = processing.run('native:creategrid', grid_params) + grid_layer = grid_result['OUTPUT'] + + field_name = 'reclass_va' + if not grid_layer.fields().indexFromName(field_name) >= 0: + grid_layer.dataProvider().addAttributes([QgsField(field_name, QVariant.Int)]) + grid_layer.updateFields() + + self.dlg.WAS_status.setText("Processing...") + self.dlg.WAS_status.repaint() + + #dif_out = f"{workingDir}/temp/tempBuf.shp" + + #Buffer = processing.run( + # "native:buffer", + #{ + # "INPUT": pointUTM, + # "DISTANCE": 250, + # "SEGMENTS": 5, + # "END_CAP_STYLE": 0, + # "JOIN_STYLE": 0, + # "MITER_LIMIT": 2, + # "DISSOLVE": False, + # "SEPARATE_DISJOINT": False, + # "OUTPUT": "memory:", + #}, + #) + + # Count the number of buffers within each grid cell + #buffer_layer = Buffer['OUTPUT'] + index = QgsSpatialIndex(pointUTM.getFeatures()) + reclass_vals = {} + + for grid_feat in grid_layer.getFeatures(): + intersecting_ids = index.intersects(grid_feat.geometry().boundingBox()) + num_waterpoints = len(intersecting_ids) + + if num_waterpoints >= 2: + reclass_val = 5 + elif num_waterpoints == 1: + reclass_val = 3 + else: + reclass_val = 0 + + reclass_vals[grid_feat.id()] = reclass_val + + grid_layer.startEditing() + for grid_feat in grid_layer.getFeatures(): + grid_layer.changeAttributeValue(grid_feat.id(), grid_layer.fields().indexFromName(field_name), + reclass_vals[grid_feat.id()]) + grid_layer.commitChanges() + + #dif_out = f"{workingDir}/{tempDir}/Dif.shp" + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": grid_layer, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [grid_layer, difference], "CRS": UTM_crs, "OUTPUT": "memory:"}, + ) + + merge = Merge["OUTPUT"] + + dif_out = f"{workingDir}/temp/Dif.shp" + mergeClip = processing.run( + "native:clip", + { + "INPUT": merge, + "OVERLAY": countryUTMLayer, + "OUTPUT": "memory:", + } + ) + + mergeOutput = mergeClip["OUTPUT"] + + # Get the width and height of the extent + + extent = countryUTMLayerBuf.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + Dimension = "Place Characterization" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + rasOutput = self.dlg.WAS_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": field_name, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.WAS_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.WAS_status.setText("Processing Complete!") + self.dlg.WAS_status.repaint() + + def ServiceArea(self, factor_no): + """ + This function is used to conduct a service area network analysis facilitated using Openrouteservices' (ORS) isochrones service. Isochrones are derived from the OpenStreetMap (OSM) road network + with the use of travel distances or times as input to estimate ease of access to the input locations at five incrementally increasing measurement intervals. The largest interval being the + maximum distance or time taken to reach these locations. The algorithm produces five catchment areas based on the road network. A scoring system ranging from 5 to 1 is assigned to the catchment area + polygons, reflecting the decreasing order of accessibility. Areas outside these catchment areas receive a score of zero. + + Service Area Analysis is undertaken using the openrouteservice API and OpenStreetMap data. © openrouteservice.org by HeiGIT | Road network data © OpenStreetMap contributors + + Factors it is applied: + Accessibility Dimension + - Women's Travel Patterns + - Access to Public Transport + - Access to Education and Training Facilities + - Access to Health Facilities + - Access to Finance Facilities + """ + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + tempDir = f"{workingDir}temp" + Dimension = "Accessibility" + + if os.path.exists(Dimension): + pass + else: + os.mkdir(Dimension) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(1) + os.mkdir(tempDir) + + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + + # INPUT + if factor_no == 0: + self.dlg.PBT_status.setText("") + self.dlg.PBT_status.repaint() + FaciltyPointlayer = self.dlg.PBT_Input_Field.filePath() + ranges = self.dlg.PBT_Ranges_Field.text() + rasOutput = f"{self.dlg.PBT_Output_Field.text()}" + mergeOutput = ( + f"{workingDir}{Dimension}/SA_SHP/{rasOutput[:-4]}_Service_Area.shp" + ) + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + self.dlg.PBT_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + if self.dlg.PBT_mode_CB.currentText() == "Driving": + mode = 0 + elif self.dlg.PBT_mode_CB.currentText() == "Walking": + mode = 6 + + if self.dlg.PBT_measurement_CB.currentText() == "Time": + measurement = 0 + ranges_field = "AA_MINS" + elif self.dlg.PBT_measurement_CB.currentText() == "Distance": + measurement = 1 + ranges_field = "AA_METERS" + + self.dlg.PBT_status.setText("Variables Set") + self.dlg.PBT_status.repaint() + time.sleep(0.5) + self.dlg.PBT_status.setText("Processing...") + self.dlg.PBT_status.repaint() + + elif factor_no == 1: + self.dlg.ETF_status.setText("") + self.dlg.ETF_status.repaint() + FaciltyPointlayer = self.dlg.ETF_Input_Field.filePath() + ranges = self.dlg.ETF_Ranges_Field.text() + rasOutput = f"{self.dlg.ETF_Output_Field.text()}" + mergeOutput = ( + f"{workingDir}{Dimension}/SA_SHP/{rasOutput[:-4]}_Service_Area.shp" + ) + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + self.dlg.ETF_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + if self.dlg.ETF_mode_CB.currentText() == "Driving": + mode = 0 + elif self.dlg.ETF_mode_CB.currentText() == "Walking": + mode = 6 + + if self.dlg.ETF_measurement_CB.currentText() == "Time": + measurement = 0 + ranges_field = "AA_MINS" + elif self.dlg.ETF_measurement_CB.currentText() == "Distance": + measurement = 1 + ranges_field = "AA_METERS" + + self.dlg.ETF_status.setText("Variables Set") + self.dlg.ETF_status.repaint() + time.sleep(0.5) + self.dlg.ETF_status.setText("Processing...") + self.dlg.ETF_status.repaint() + + elif factor_no == 3: + self.dlg.HEA_status.setText("") + self.dlg.HEA_status.repaint() + FaciltyPointlayer = self.dlg.HEA_Input_Field.filePath() + ranges = self.dlg.HEA_Ranges_Field.text() + rasOutput = f"{self.dlg.HEA_Output_Field.text()}" + mergeOutput = ( + f"{workingDir}{Dimension}/SA_SHP/{rasOutput[:-4]}_Service_Area.shp" + ) + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + self.dlg.HEA_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + if self.dlg.HEA_mode_CB.currentText() == "Driving": + mode = 0 + elif self.dlg.HEA_mode_CB.currentText() == "Walking": + mode = 6 + + if self.dlg.HEA_measurement_CB.currentText() == "Time": + measurement = 0 + ranges_field = "AA_MINS" + elif self.dlg.HEA_measurement_CB.currentText() == "Distance": + measurement = 1 + ranges_field = "AA_METERS" + + self.dlg.HEA_status.setText("Variables Set") + self.dlg.HEA_status.repaint() + time.sleep(0.5) + self.dlg.HEA_status.setText("Processing...") + self.dlg.HEA_status.repaint() + + elif factor_no == 4: + self.dlg.FIF_status.setText("") + self.dlg.FIF_status.repaint() + FaciltyPointlayer = self.dlg.FIF_Input_Field.filePath() + ranges = self.dlg.FIF_Ranges_Field.text() + rasOutput = f"{self.dlg.FIF_Output_Field.text()}" + mergeOutput = ( + f"{workingDir}{Dimension}/SA_SHP/{rasOutput[:-4]}_Service_Area.shp" + ) + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + self.dlg.FIF_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + if self.dlg.FIF_mode_CB.currentText() == "Driving": + mode = 0 + elif self.dlg.FIF_mode_CB.currentText() == "Walking": + mode = 6 + + if self.dlg.FIF_measurement_CB.currentText() == "Time": + measurement = 0 + ranges_field = "AA_MINS" + elif self.dlg.FIF_measurement_CB.currentText() == "Distance": + measurement = 1 + ranges_field = "AA_METERS" + + self.dlg.FIF_status.setText("Variables Set") + self.dlg.FIF_status.repaint() + time.sleep(0.5) + self.dlg.FIF_status.setText("Processing...") + self.dlg.FIF_status.repaint() + + elif factor_no == 5: + self.dlg.WTP_status.setText("") + self.dlg.WTP_status.repaint() + input_fields = [ + self.dlg.WTP_Input_Field, + self.dlg.WTP_Input_Field_2, + self.dlg.WTP_Input_Field_3, + self.dlg.WTP_Input_Field_4 + ] + + FaciltyPointlayerList = [] + + for input_field in input_fields: + file_path = input_field.filePath() + if file_path: + FaciltyPointlayerList.append(file_path) + + ranges = self.dlg.WTP_Ranges_Field.text() + rasOutput = f"{self.dlg.WTP_FacilityOutput_Field.text()}" + mergeOutput = ( + f"{workingDir}{Dimension}/SA_SHP/{rasOutput[:-4]}_Service_Area.shp" + ) + + if self.dlg.WTP_mode_CB.currentText() == "Driving": + mode = 0 + elif self.dlg.WTP_mode_CB.currentText() == "Walking": + mode = 6 + + if self.dlg.WTP_measurement_CB.currentText() == "Time": + measurement = 0 + ranges_field = "AA_MINS" + elif self.dlg.WTP_measurement_CB.currentText() == "Distance": + measurement = 1 + ranges_field = "AA_METERS" + + self.dlg.WTP_status.setText("Variables Set") + self.dlg.WTP_status.repaint() + time.sleep(0.5) + self.dlg.WTP_status.setText("Processing...") + self.dlg.WTP_status.repaint() + + # OUTPUT + SAOutput_utm = f"{tempDir}/SA_OUTPUT_UTM.shp" + temp_merge = f"{tempDir}/temp_merge.shp" + + if factor_no == 5: + # Do stuff + facility_data = 0 + for FaciltyPointlayer in FaciltyPointlayerList: + os.chdir(workingDir) + rasOutput = "" + base_name = os.path.basename(FaciltyPointlayer) + shapefile_name, _ = os.path.splitext(base_name) + gdf = gpd.read_file(FaciltyPointlayer) + + subset_size = 5 + subsets = [] + + for i in range(0, len(gdf), subset_size): + subset = gdf.iloc[i: i + subset_size] + subset = QgsVectorLayer(subset.to_json(), "mygeojson", "ogr") + subset_outfile = ( + f"{tempDir}/SA_subset_{i + subset_size}_{rasOutput[:-4]}.shp" + ) + + Service_Area = processing.run( + "ORS Tools:isochrones_from_layer", + { + "INPUT_PROVIDER": 0, + "INPUT_PROFILE": mode, + "INPUT_POINT_LAYER": subset, + "INPUT_FIELD": "", + "INPUT_METRIC": measurement, + "INPUT_RANGES": ranges, + "INPUT_SMOOTHING": None, + "LOCATION_TYPE": 0, + "INPUT_AVOID_FEATURES": [], + "INPUT_AVOID_BORDERS": None, + "INPUT_AVOID_COUNTRIES": "", + "INPUT_AVOID_POLYGONS": None, + "OUTPUT": subset_outfile, + }, + ) + + subsets.append(subset_outfile) + + batch = i + subset_size + + if batch > len(gdf): + batch = len(gdf) + + if factor_no == 0: + self.dlg.PBT_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.PBT_status.repaint() + elif factor_no == 1: + self.dlg.ETF_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.ETF_status.repaint() + elif factor_no == 3: + self.dlg.HEA_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.HEA_status.repaint() + elif factor_no == 4: + self.dlg.FIF_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.FIF_status.repaint() + elif factor_no == 5: + self.dlg.WTP_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.WTP_status.repaint() + + Merge = processing.run( + "native:mergevectorlayers", + { + "LAYERS": subsets, + "CRS": QgsCoordinateReferenceSystem(UTM_crs), + "OUTPUT": SAOutput_utm, + }, + ) + + time.sleep(0.5) + + # self.convertCRS(SAOutput_utm, UTM_crs) + # SA_df = shp_utm + SA_df = gpd.read_file(SAOutput_utm) + no_spaces_string = "".join(ranges.split()) + ranges_list = no_spaces_string.split(",") + int_ranges_list = [int(x) for x in ranges_list] + int_ranges_list.sort() + + range_subsets = [] + + for i in int_ranges_list: + range_subset = SA_df[SA_df[ranges_field] == i] + range_subset = QgsVectorLayer(range_subset.to_json(), f"range_{i}", "ogr") + temp_out = f"{tempDir}/{rasOutput[:-4]}Range_dis_{i}.shp" + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": range_subset, + "FIELD": [], + "SEPARATE_DISJOINT": False, + "OUTPUT": temp_out, + }, + ) + + Range_output = dissolve["OUTPUT"] + + range_subsets.append(range_subset) + + Merge_list = [] + + for i in range(-1, -len(range_subsets), -1): + output = ( + f"{tempDir}/band_dif_{int_ranges_list[i]}_-_{int_ranges_list[i - 1]}.shp" + ) + # Merge_list.append(output) + difference = processing.run( + "native:difference", + { + "INPUT": range_subsets[i], + "OVERLAY": range_subsets[i - 1], + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + diff_output = difference["OUTPUT"] + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": diff_output, + "FIELD": [], + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + dis_output = dissolve["OUTPUT"] + Merge_list.append(dis_output) + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": range_subsets[0], + "FIELD": [], + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + dis_output = dissolve["OUTPUT"] + Merge_list.append(dis_output) + + print(f"Path: {os.getcwd()}") + + if os.path.exists(f"{Dimension}/SA_SHP"): + pass + else: + os.mkdir(f"{Dimension}/SA_SHP") + + feedback = QgsProcessingFeedback() + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": Merge_list, "CRS": None, "OUTPUT": f"{mergeOutput}"}, + ) + + if Merge is not None and "OUTPUT" in Merge: + print(f"Processing completed successfully") + + # Do something + + merge_df = gpd.read_file(f"{mergeOutput}") + merge_df["rasField"] = [1, 2, 3, 4, 5] + merge_SA_UTM = QgsVectorLayer(merge_df.to_json(), "merge_SA_utm", "ogr") + + # Convert countryLayer data to UTM CRS + self.convertCRS(countryLayer, UTM_crs) + shp_utm["rasField"] = [0] + shp_utm_ = QgsVectorLayer(shp_utm.to_json(), "shp_utm", "ogr") + # shp_utm.to_file(countryUTMLayer) + + buffer = processing.run( + "native:buffer", + { + "INPUT": shp_utm_, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + buffer_output = buffer["OUTPUT"] + + clipAOI = processing.run( + "native:clip", + { + "INPUT": merge_SA_UTM, + "OVERLAY": buffer_output, + "OUTPUT": "memory:", + } + ) + + merge_SA_UTM = clipAOI["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": buffer_output, + "OVERLAY": merge_SA_UTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + diff_output = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [merge_SA_UTM, diff_output], "CRS": None, "OUTPUT": "memory:"}, + ) + + merge_output = Merge["OUTPUT"] + + extent = merge_output.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + rasOutput = f"{self.dlg.WTP_FacilityOutput_Field.text()[:-4]}{shapefile_name}.tif" + + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + if factor_no == 5: + Output_Folder = "WTP" + if os.path.exists(Output_Folder): + os.chdir(Output_Folder) + else: + os.mkdir(Output_Folder) + os.chdir(Output_Folder) + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/{Output_Folder}" + styleFile = f"{rasOutput.split('.')[0]}.qml" + else: + pass + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": merge_output, + "FIELD": "rasField", + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + facility_data = facility_data + 1 + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + if factor_no == 0: + self.dlg.PBT_status.setText("Processing Complete!") + self.dlg.PBT_status.repaint() + + elif factor_no == 1: + self.dlg.ETF_status.setText("Processing Complete!") + self.dlg.ETF_status.repaint() + + elif factor_no == 3: + self.dlg.HEA_status.setText("Processing Complete!") + self.dlg.HEA_status.repaint() + + elif factor_no == 4: + self.dlg.FIF_status.setText("Processing Complete!") + self.dlg.FIF_status.repaint() + + elif factor_no == 5: + self.dlg.WTP_status.setText("Processing Complete!") + self.dlg.WTP_status.repaint() + else: + print(f"Processing failed") + else: + # Do more stuff + + gdf = gpd.read_file(FaciltyPointlayer) + + subset_size = 5 + subsets = [] + + for i in range(0, len(gdf), subset_size): + subset = gdf.iloc[i: i + subset_size] + print(f"subset:{subset}") + subset = QgsVectorLayer(subset.to_json(), "mygeojson", "ogr") + subset_outfile = ( + f"{tempDir}/SA_subset_{i + subset_size}_{rasOutput[:-4]}.shp" + ) + + Service_Area = processing.run( + "ORS Tools:isochrones_from_layer", + { + "INPUT_PROVIDER": 0, + "INPUT_PROFILE": mode, + "INPUT_POINT_LAYER": subset, + "INPUT_FIELD": "", + "INPUT_METRIC": measurement, + "INPUT_RANGES": ranges, + "INPUT_SMOOTHING": None, + "LOCATION_TYPE": 0, + "INPUT_AVOID_FEATURES": [], + "INPUT_AVOID_BORDERS": None, + "INPUT_AVOID_COUNTRIES": "", + "INPUT_AVOID_POLYGONS": None, + "OUTPUT": subset_outfile, + }, + ) + + subsets.append(subset_outfile) + + batch = i + subset_size + + if batch > len(gdf): + batch = len(gdf) + + if factor_no == 0: + self.dlg.PBT_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.PBT_status.repaint() + elif factor_no == 1: + self.dlg.ETF_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.ETF_status.repaint() + elif factor_no == 3: + self.dlg.HEA_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.HEA_status.repaint() + elif factor_no == 4: + self.dlg.FIF_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.FIF_status.repaint() + elif factor_no == 5: + self.dlg.WTP_status.setText(f"Processing... {batch} of {len(gdf)}") + self.dlg.WTP_status.repaint() + + Merge = processing.run( + "native:mergevectorlayers", + { + "LAYERS": subsets, + "CRS": QgsCoordinateReferenceSystem(UTM_crs), + "OUTPUT": SAOutput_utm, + }, + ) + + time.sleep(0.5) + + # self.convertCRS(SAOutput_utm, UTM_crs) + # SA_df = shp_utm + SA_df = gpd.read_file(SAOutput_utm) + no_spaces_string = "".join(ranges.split()) + ranges_list = no_spaces_string.split(",") + int_ranges_list = [int(x) for x in ranges_list] + int_ranges_list.sort() + + range_subsets = [] + + for i in int_ranges_list: + range_subset = SA_df[SA_df[ranges_field] == i] + range_subset = QgsVectorLayer(range_subset.to_json(), f"range_{i}", "ogr") + temp_out = f"{tempDir}/{rasOutput[:-4]}Range_dis_{i}.shp" + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": range_subset, + "FIELD": [], + "SEPARATE_DISJOINT": False, + "OUTPUT": temp_out, + }, + ) + + Range_output = dissolve["OUTPUT"] + + range_subsets.append(range_subset) + + Merge_list = [] + + for i in range(-1, -len(range_subsets), -1): + output = ( + f"{tempDir}/band_dif_{int_ranges_list[i]}_-_{int_ranges_list[i - 1]}.shp" + ) + # Merge_list.append(output) + difference = processing.run( + "native:difference", + { + "INPUT": range_subsets[i], + "OVERLAY": range_subsets[i - 1], + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + diff_output = difference["OUTPUT"] + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": diff_output, + "FIELD": [], + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + dis_output = dissolve["OUTPUT"] + Merge_list.append(dis_output) + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": range_subsets[0], + "FIELD": [], + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + dis_output = dissolve["OUTPUT"] + Merge_list.append(dis_output) + + if os.path.exists(f"{Dimension}/SA_SHP"): + pass + else: + os.mkdir(f"{Dimension}/SA_SHP") + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": Merge_list, "CRS": None, "OUTPUT": f"{mergeOutput}"}, + ) + + time.sleep(0.5) + + merge_df = gpd.read_file(f"{mergeOutput}") + merge_df["rasField"] = [1, 2, 3, 4, 5] + merge_SA_UTM = QgsVectorLayer(merge_df.to_json(), "merge_SA_utm", "ogr") + + # Convert countryLayer data to UTM CRS + self.convertCRS(countryLayer, UTM_crs) + shp_utm["rasField"] = [0] + shp_utm_ = QgsVectorLayer(shp_utm.to_json(), "shp_utm", "ogr") + # shp_utm.to_file(countryUTMLayer) + + buffer = processing.run( + "native:buffer", + { + "INPUT": shp_utm_, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + buffer_output = buffer["OUTPUT"] + + clipAOI = processing.run( + "native:clip", + { + "INPUT": merge_SA_UTM, + "OVERLAY": buffer_output, + "OUTPUT": "memory:", + } + ) + + merge_SA_UTM = clipAOI["OUTPUT"] + + Difference = processing.run( + "native:difference", + { + "INPUT": buffer_output, + "OVERLAY": merge_SA_UTM, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + diff_output = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [merge_SA_UTM, diff_output], "CRS": None, "OUTPUT": "memory:"}, + ) + + merge_output = Merge["OUTPUT"] + + extent = merge_output.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + if factor_no == 5: + Output_Folder = "WTP" + if os.path.exists(Output_Folder): + os.chdir(Output_Folder) + else: + os.mkdir(Output_Folder) + os.chdir(Output_Folder) + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/{Output_Folder}" + styleFile = f"{rasOutput.split('.')[0]}.qml" + else: + pass + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": merge_output, + "FIELD": "rasField", + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + if factor_no == 0: + self.dlg.PBT_status.setText("Processing Complete!") + self.dlg.PBT_status.repaint() + + elif factor_no == 1: + self.dlg.ETF_status.setText("Processing Complete!") + self.dlg.ETF_status.repaint() + + elif factor_no == 3: + self.dlg.HEA_status.setText("Processing Complete!") + self.dlg.HEA_status.repaint() + + elif factor_no == 4: + self.dlg.FIF_status.setText("Processing Complete!") + self.dlg.FIF_status.repaint() + + elif factor_no == 5: + self.dlg.WTP_status.setText("Processing Complete!") + self.dlg.WTP_status.repaint() + + def wtpAggregate(self): + """ + This function is used in combination with the "ServiceArea" function. Due to women's travel patterns involving numerous locations and/or facilities a service area network analysis + has to be conducted numerous times. This function aggregates each of the service area analysis relating to women's travel patterns into a single standardized raster file. + + Factors it is applied: + Accessibility Dimension + - Women's Travel Patterns + """ + self.dlg.WTPAGG_status.setText("") + self.dlg.WTPAGG_status.repaint() + # OUTPUT + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + Dimension = "Accessibility" + WTP_Folder = f"{Dimension}/WTP" + + if os.path.exists(WTP_Folder): + os.chdir(WTP_Folder) + else: + pass + + rasOutput = self.dlg.WTP_AGGOutput_Field.text() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + tif_list = [f for f in os.listdir(os.getcwd()) if f.endswith(".tif")] + raster_list = [] + + for ras in tif_list: + with rasterio.open(ras) as src: + raster_list.append(src.read(1)) + meta1 = src.meta + + len_raster_list = len(raster_list) + cumulative_sum = 0 + + for i in range(len_raster_list): + value = raster_list[i] + cumulative_sum += value + + aggregation = cumulative_sum / 4 + os.chdir("..") + + with rasterio.open(rasOutput, "w", **meta1) as dst: + dst.write(aggregation, 1) + + self.dlg.WTP_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.WTPAGG_status.setText("Processing Complete!") + self.dlg.WTPAGG_status.repaint() + + os.chdir(workingDir) + + def secAggregate(self): + """ + This function is used in combination with the "Rasterization" function. Due to security, or lack thereof, involving numerous incident types, rasterization of each type + has to be conducted. This function aggregates each of the rasterized incident types relating to security into a single standardized raster file. + + Factors it is applied: + Place Characterization Dimension + - Security + """ + self.dlg.SECAGG_status.setText("") + self.dlg.SECAGG_status.repaint() + # OUTPUT + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + Dimension = "Place Characterization" + SEC_Folder = f"{Dimension}/SEC" + + if os.path.exists(SEC_Folder): + os.chdir(SEC_Folder) + else: + pass + + rasOutput = self.dlg.SEC_AGGOutput_Field.text() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + tif_list = [f for f in os.listdir(os.getcwd()) if f.endswith(".tif")] + raster_list = [] + + for ras in tif_list: + with rasterio.open(ras) as src: + raster_list.append(src.read(1)) + meta1 = src.meta + + len_raster_list = len(raster_list) + cumulative_sum = 0 + + for i in range(len_raster_list): + value = raster_list[i] + cumulative_sum += value + + aggregation = cumulative_sum / len_raster_list + os.chdir("..") + + with rasterio.open(rasOutput, "w", **meta1) as dst: + dst.write(aggregation, 1) + + self.dlg.SEC_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.SECAGG_status.setText("Processing Complete!") + self.dlg.SECAGG_status.repaint() + + os.chdir(workingDir) + + def walkability(self): + """ + This function is used in combination with the "TypeSet" and "uniqueValue" functions to execute the Active Transports rasterization algorithm. + It buffers the road network by 250m and rasterizes the buffer polygons according to the scoring assigned to the unique values. + + Factors it is applied: + Place Characterization Dimension + - Active Transport + """ + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + tempDir = "temp" + Dimension = "Place Characterization" + + if os.path.exists(Dimension): + pass + else: + os.mkdir(Dimension) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + streetCrossingLayer = self.dlg.WLK_Input_Field.filePath() + blockLayer = self.dlg.WLK_Input_Field_4.filePath() + input_fields = [ + self.dlg.WLK_Input_Field_2, + self.dlg.WLK_Input_Field_3 + ] + + lineLayerList = [] + + for input_field in input_fields: + file_path = input_field.filePath() + if file_path: + lineLayerList.append(file_path) + #roadTypeField = self.dlg.WLK_roadTypeField_CB.currentText() + #roadType_Score = ast.literal_eval(self.dlg.WLK_typeScore_Field.text()) + rasField = "Score" + + self.dlg.WLK_status_2.setText("Variables Set") + self.dlg.WLK_status_2.repaint() + time.sleep(0.5) + self.dlg.WLK_status_2.setText("Processing...") + self.dlg.WLK_status_2.repaint() + + self.convertCRS(countryLayer, UTM_crs) + shp_utm[rasField] = [0] + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + if lineLayerList is not None: + for lineLayer in lineLayerList: + os.chdir(workingDir) + + base_name = os.path.basename(lineLayer) + shapefile_name, _ = os.path.splitext(base_name) + scoredRoads = f"{workingDir}/{tempDir}/Scored_roads_{shapefile_name}.shp" + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + countryUTMLayerBuf = buffer["OUTPUT"] + + self.convertCRS(lineLayer, UTM_crs) + #shp_utm[rasField] = [0] + + #for i in roadType_Score: + # shp_utm.loc[shp_utm[roadTypeField] == i[0], "Score"] = i[1] + + #shp_utm[rasField] = shp_utm[rasField].astype(int) + shp_utm.to_file(scoredRoads) + + gridOutput = f"{workingDir}{tempDir}/grid.shp" + gridExtent = countryUTMLayer.extent() + grid_params = { + 'TYPE': 2, # Rectangle (polygon) + 'EXTENT': gridExtent, + 'HSPACING': 100, # Horizontal spacing + 'VSPACING': 100, # Vertical spacing + 'CRS': UTM_crs, + 'OUTPUT': "memory:" + } + + grid_result = processing.run('native:creategrid', grid_params) + grid_layer = grid_result['OUTPUT'] + + field_name = 'reclass_va' + if not grid_layer.fields().indexFromName(field_name) >= 0: + grid_layer.dataProvider().addAttributes([QgsField(field_name, QVariant.Int)]) + grid_layer.updateFields() + + # scoredRoadsUTM = QgsVectorLayer(shp_utm.to_json(), "linebufUTM", "ogr") + roadBuf_out = f"{workingDir}/{tempDir}/roadBuf.shp" + + self.dlg.WLK_status_2.setText(f"Processing... {shapefile_name}") + self.dlg.WLK_status_2.repaint() + + Buffer = processing.run( + "gdal:buffervectors", + { + "INPUT": scoredRoads, + "GEOMETRY": "geometry", + "DISTANCE": 50, + "FIELD": "", + "DISSOLVE": False, + "EXPLODE_COLLECTIONS": False, + "OPTIONS": "", + "OUTPUT": roadBuf_out, + }, + ) + + # Count the number of buffers within each grid cell + line_layer = QgsVectorLayer(scoredRoads, 'buffer', 'ogr') + index = QgsSpatialIndex(line_layer.getFeatures()) + reclass_vals = {} + + for grid_feat in grid_layer.getFeatures(): + intersecting_ids = index.intersects(grid_feat.geometry().boundingBox()) + # Initialize a set to store unique intersecting line feature IDs + unique_intersections = set() + + # Check each potentially intersecting line feature + for line_id in intersecting_ids: + line_feat = line_layer.getFeature(line_id) + line_geom = line_feat.geometry() + + # Perform a detailed intersection check + if grid_feat.geometry().intersects(line_geom): + unique_intersections.add(line_id) # Add the line feature ID to the set + + # Count the number of unique intersecting line features + num_footpaths = len(unique_intersections) + + if num_footpaths >= 2: + reclass_val = 5 + elif num_footpaths == 1: + reclass_val = 3 + else: + reclass_val = 0 + + reclass_vals[grid_feat.id()] = reclass_val + + grid_layer.startEditing() + for grid_feat in grid_layer.getFeatures(): + grid_layer.changeAttributeValue(grid_feat.id(), grid_layer.fields().indexFromName(field_name), + reclass_vals[grid_feat.id()]) + grid_layer.commitChanges() + + dif_out = f"{workingDir}/{tempDir}/Dif.shp" + + #Difference = processing.run( + # "native:difference", + # { + # "INPUT": countryUTMLayerBuf, + # "OVERLAY": grid_layer, + # "OUTPUT": dif_out, + # "GRID_SIZE": None, + # }, + #) + + # difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [grid_layer], "CRS": None, "OUTPUT": "memory:"}, + ) + + merge = Merge["OUTPUT"] + + mergeClip = processing.run( + "native:clip", + { + "INPUT": merge, + "OVERLAY": countryUTMLayer, + "OUTPUT": "memory:", + } + ) + + mergeOutput = mergeClip["OUTPUT"] + + # Get the width and height of the extent + extent = countryUTMLayerBuf.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + os.chdir(Dimension) + Output_Folder = "AT" + if os.path.exists(Output_Folder): + os.chdir(Output_Folder) + else: + os.mkdir(Output_Folder) + os.chdir(Output_Folder) + rasOutput = f"{self.dlg.WLK_Output_Field_2.text()[:-4]}{shapefile_name}.tif" + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": field_name, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + #self.dlg.WLK_Output_Field_2.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/{Output_Folder}" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.WLK_status_2.setText(f"Processing Complete for {shapefile_name}!") + self.dlg.WLK_status_2.repaint() + else: + pass + if streetCrossingLayer: + os.chdir(workingDir) + + base_name = os.path.basename(streetCrossingLayer) + shapefile_name, _ = os.path.splitext(base_name) + scoredRoads = f"{workingDir}/{tempDir}/Scored_{shapefile_name}.shp" + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + countryUTMLayerBuf = buffer["OUTPUT"] + + self.convertCRS(streetCrossingLayer, UTM_crs) + #shp_utm[rasField] = [0] + + #for i in roadType_Score: + # shp_utm.loc[shp_utm[roadTypeField] == i[0], "Score"] = i[1] + + #shp_utm[rasField] = shp_utm[rasField].astype(int) + shp_utm.to_file(scoredRoads) + + gridOutput = f"{workingDir}{tempDir}/grid.shp" + gridExtent = countryUTMLayer.extent() + grid_params = { + 'TYPE': 2, # Rectangle (polygon) + 'EXTENT': gridExtent, + 'HSPACING': 100, # Horizontal spacing + 'VSPACING': 100, # Vertical spacing + 'CRS': UTM_crs, + 'OUTPUT': "memory:" + } + + grid_result = processing.run('native:creategrid', grid_params) + grid_layer = grid_result['OUTPUT'] + + field_name = 'reclass_va' + if not grid_layer.fields().indexFromName(field_name) >= 0: + grid_layer.dataProvider().addAttributes([QgsField(field_name, QVariant.Int)]) + grid_layer.updateFields() + + # scoredRoadsUTM = QgsVectorLayer(shp_utm.to_json(), "linebufUTM", "ogr") + #roadBuf_out = f"{workingDir}/{tempDir}/roadBuf.shp" + + self.dlg.WLK_status_2.setText(f"Processing... {shapefile_name}") + self.dlg.WLK_status_2.repaint() + + #Buffer = processing.run( + # "gdal:buffervectors", + # { + # "INPUT": scoredRoads, + # "GEOMETRY": "geometry", + # "DISTANCE": 50, + # "FIELD": "", + # "DISSOLVE": False, + # "EXPLODE_COLLECTIONS": False, + # "OPTIONS": "", + # "OUTPUT": roadBuf_out, + # }, + #) + + # Count the number of buffers within each grid cell + point_layer = QgsVectorLayer(scoredRoads, 'buffer', 'ogr') + index = QgsSpatialIndex(point_layer.getFeatures()) + reclass_vals = {} + + for grid_feat in grid_layer.getFeatures(): + intersecting_ids = index.intersects(grid_feat.geometry().boundingBox()) + num_footpaths = len(intersecting_ids) + + if num_footpaths >= 2: + reclass_val = 5 + elif num_footpaths == 1: + reclass_val = 3 + else: + reclass_val = 0 + + reclass_vals[grid_feat.id()] = reclass_val + + grid_layer.startEditing() + for grid_feat in grid_layer.getFeatures(): + grid_layer.changeAttributeValue(grid_feat.id(), grid_layer.fields().indexFromName(field_name), + reclass_vals[grid_feat.id()]) + grid_layer.commitChanges() + + dif_out = f"{workingDir}/{tempDir}/Dif.shp" + + #Difference = processing.run( + # "native:difference", + # { + # "INPUT": countryUTMLayerBuf, + # "OVERLAY": grid_layer, + # "OUTPUT": dif_out, + # "GRID_SIZE": None, + # }, + #) + + # difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [grid_layer], "CRS": None, "OUTPUT": "memory:"}, + ) + + merge = Merge["OUTPUT"] + + mergeClip = processing.run( + "native:clip", + { + "INPUT": merge, + "OVERLAY": countryUTMLayer, + "OUTPUT": "memory:", + } + ) + + mergeOutput = mergeClip["OUTPUT"] + + # Get the width and height of the extent + extent = countryUTMLayerBuf.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + os.chdir(Dimension) + Output_Folder = "AT" + if os.path.exists(Output_Folder): + os.chdir(Output_Folder) + else: + os.mkdir(Output_Folder) + os.chdir(Output_Folder) + rasOutput = f"{self.dlg.WLK_Output_Field_2.text()[:-4]}{shapefile_name}.tif" + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": field_name, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + #self.dlg.WLK_Output_Field_2.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/{Output_Folder}" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.WLK_status_2.setText(f"Processing Complete for {shapefile_name}!") + self.dlg.WLK_status_2.repaint() + else: + pass + if blockLayer: + os.chdir(workingDir) + + base_name = os.path.basename(blockLayer) + shapefile_name, _ = os.path.splitext(base_name) + scoredBlocks = f"{workingDir}/{tempDir}/Scored_blocks_{shapefile_name}.shp" + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + countryUTMLayerBuf = buffer["OUTPUT"] + + self.convertCRS(blockLayer, UTM_crs) + #shp_utm[rasField] = [0] + + #for i in roadType_Score: + # shp_utm.loc[shp_utm[roadTypeField] == i[0], "Score"] = i[1] + + #shp_utm[rasField] = shp_utm[rasField].astype(int) + shp_utm.to_file(scoredBlocks) + + gridOutput = f"{workingDir}{tempDir}/grid.shp" + gridExtent = countryUTMLayer.extent() + grid_params = { + 'TYPE': 2, # Rectangle (polygon) + 'EXTENT': gridExtent, + 'HSPACING': 100, # Horizontal spacing + 'VSPACING': 100, # Vertical spacing + 'CRS': UTM_crs, + 'OUTPUT': "memory:" + } + + grid_result = processing.run('native:creategrid', grid_params) + grid_layer = grid_result['OUTPUT'] + + field_name = 'reclass_va' + if not grid_layer.fields().indexFromName(field_name) >= 0: + grid_layer.dataProvider().addAttributes([QgsField(field_name, QVariant.Int)]) + grid_layer.updateFields() + + # scoredRoadsUTM = QgsVectorLayer(shp_utm.to_json(), "linebufUTM", "ogr") + roadBuf_out = f"{workingDir}/{tempDir}/roadBuf.shp" + + self.dlg.WLK_status_2.setText(f"Processing... {shapefile_name}") + self.dlg.WLK_status_2.repaint() + + Buffer = processing.run( + "gdal:buffervectors", + { + "INPUT": scoredBlocks, + "GEOMETRY": "geometry", + "DISTANCE": 50, + "FIELD": "", + "DISSOLVE": False, + "EXPLODE_COLLECTIONS": False, + "OPTIONS": "", + "OUTPUT": roadBuf_out, + }, + ) + + # Count the number of buffers within each grid cell + block_layer = QgsVectorLayer(scoredBlocks, 'buffer', 'ogr') + index = QgsSpatialIndex(block_layer.getFeatures()) + reclass_vals = {} + + for grid_feat in grid_layer.getFeatures(): + intersecting_ids = index.intersects(grid_feat.geometry().boundingBox()) + # Initialize a set to store unique intersecting polygon feature IDs + unique_intersections = set() + + # Check each potentially intersecting polygon feature + for poly_id in intersecting_ids: + poly_feat = block_layer.getFeature(poly_id) + poly_geom = poly_feat.geometry() + + # Perform a detailed intersection check + if grid_feat.geometry().intersects(poly_geom): + unique_intersections.add(poly_id) + + # Count the number of unique intersecting polygon features + num_blocks = len(unique_intersections) + + if num_blocks >= 2: + reclass_val = 5 + elif num_blocks == 1: + reclass_val = 3 + else: + reclass_val = 0 + + reclass_vals[grid_feat.id()] = reclass_val + + grid_layer.startEditing() + for grid_feat in grid_layer.getFeatures(): + grid_layer.changeAttributeValue(grid_feat.id(), grid_layer.fields().indexFromName(field_name), + reclass_vals[grid_feat.id()]) + grid_layer.commitChanges() + + dif_out = f"{workingDir}/{tempDir}/Dif.shp" + + #Difference = processing.run( + # "native:difference", + # { + # "INPUT": countryUTMLayerBuf, + # "OVERLAY": grid_layer, + # "OUTPUT": dif_out, + # "GRID_SIZE": None, + # }, + #) + + # difference = Difference["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [grid_layer], "CRS": None, "OUTPUT": "memory:"}, + ) + + merge = Merge["OUTPUT"] + + mergeClip = processing.run( + "native:clip", + { + "INPUT": merge, + "OVERLAY": countryUTMLayer, + "OUTPUT": "memory:", + } + ) + + mergeOutput = mergeClip["OUTPUT"] + + # Get the width and height of the extent + extent = countryUTMLayerBuf.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + os.chdir(Dimension) + Output_Folder = "AT" + if os.path.exists(Output_Folder): + os.chdir(Output_Folder) + else: + os.mkdir(Output_Folder) + os.chdir(Output_Folder) + rasOutput = f"{self.dlg.WLK_Output_Field_2.text()[:-4]}{shapefile_name}.tif" + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": field_name, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + #self.dlg.WLK_Output_Field_2.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/{Output_Folder}" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.WLK_status_2.setText(f"Processing Complete for {shapefile_name}!") + self.dlg.WLK_status_2.repaint() + + def SAFRasterizerDelegator(self): + """ + This function delegates the rendering task to one of four rasterizing functions based on input type and subtype: + - In case of raster input --> SAFnightTimeLights + - In case of points (vector) input --> SAFstreetLightsRasterizer + - In case of polygons (vector) with numeric field as metric --> SAFPerceivedSafetyFromNumericFieldRasterizer + - In case of polygons (vector) with text field as metric --> SAFPerceivedSafetyFromTextFieldRasterizer + - If no input layer is given, uses a user-provided value to rasterize --> SAFPerceivedSafetyFromUserValueRasterizer + """ + + # Announce the start of the process + self.dlg.SAF_status.setText("Starting...") + self.dlg.SAF_status.repaint() + + input_file = self.dlg.SAF_Input_Field.filePath() + + # Check if the input file is empty or not provided + if not input_file: + # Get the user-provided value from the spinner + user_value = self.dlg.SAF_User_Value_Input.value() + self.SAFPerceivedSafetyFromUserValueRasterizer(user_value) + return + + input_layer = QgsRasterLayer(input_file, "input") + + if input_layer.isValid(): + self.SAFnightTimeLights() + else: + input_layer = QgsVectorLayer(input_file, "input", "ogr") + if not input_layer.isValid(): + self.dlg.SAF_status.setText("The input file is not a valid raster or vector layer.") + self.dlg.SAF_status.repaint() + return + + # Check the geometry type of the vector layer + if input_layer.geometryType() == QgsWkbTypes.PointGeometry: + self.SAFstreetLightsRasterizer() + elif input_layer.geometryType() == QgsWkbTypes.PolygonGeometry: + selected_field = self.dlg.SAF_rasField_CB.currentText() + if selected_field.endswith(" (text)"): + self.SAFPerceivedSafetyFromTextFieldRasterizer(input_layer) + else: + self.SAFPerceivedSafetyFromNumericFieldRasterizer(input_layer) + else: + self.dlg.SAF_status.setText( + "Unsupported vector layer type. Only point and polygon geometries are supported.") + self.dlg.SAF_status.repaint() + + def populateCBFieldsFromPolygonLayer_PC_SAF(self, file_path): + if file_path == '': + # enable the user value field if no file is selected + self.dlg.SAF_User_Value_Input.setEnabled(True) + else: + # disable the user value field + self.dlg.SAF_User_Value_Input.setEnabled(False) + + # Clear and disable the SAF_typeScore_Field by default + self.dlg.SAF_typeScore_Field.clear() + self.dlg.SAF_typeScore_Field.setEnabled(False) + layer = QgsVectorLayer(file_path, "input", "ogr") + # Check if the layer is a polygon + if layer.geometryType() == QgsWkbTypes.PolygonGeometry: + # if it is, populate the combo box fields + self.dlg.SAF_rasField_CB.setEnabled(True) + self.dlg.SAF_rasField_CB.clear() + for field in layer.fields(): + field_name = field.name() + # Check if the field is of type text + if field.type() == QVariant.String: + # distinguish text fields + field_name = f"{field_name} (text)" + self.dlg.SAF_rasField_CB.addItem(field_name) + else: + # ensure the CB is not displaying stale information + self.dlg.SAF_rasField_CB.clear() + self.dlg.SAF_rasField_CB.setEnabled(False) + # Clear and disable the SAF_typeScore_Field by default + self.dlg.SAF_typeScore_Field.clear() + self.dlg.SAF_typeScore_Field.setEnabled(False) + + def populateCBFieldsFromPolygonLayer_PC_EDU(self, file_path): + layer = QgsVectorLayer(file_path, "input", "ogr") + # Check if the layer is a polygon + if layer.geometryType() == QgsWkbTypes.PolygonGeometry: + # if it is, populate the combo box fields + self.dlg.EDU_rasField_CB.setEnabled(True) + self.dlg.EDU_rasField_CB.clear() + for field in layer.fields(): + field_name = field.name() + # Check if the field is of type text + if field.type() != QVariant.String: + # remove text fields + self.dlg.EDU_rasField_CB.addItem(field_name) + else: + # ensure the CB is not displaying stale information + self.dlg.EDU_rasField_CB.clear() + self.dlg.EDU_rasField_CB.setEnabled(False) + + def populateCBFieldsFromPolygonLayer_PC_ENV(self, file_path): + # Clear and disable the SAF_typeScore_Field by default + self.dlg.ENV_typeScore_Field.clear() + self.dlg.ENV_typeScore_Field.setEnabled(False) + layer = QgsVectorLayer(file_path, "input", "ogr") + # Check if the layer is a polygon + if layer.geometryType() == QgsWkbTypes.PolygonGeometry: + # if it is, populate the combo box fields + self.dlg.ENV_riskLevelField_CB.setEnabled(True) + self.dlg.ENV_riskLevelField_CB.clear() + for field in layer.fields(): + field_name = field.name() + # Check if the field is of type text + if field.type() == QVariant.String: + # distinguish text fields + field_name = f"{field_name} (text)" + self.dlg.ENV_riskLevelField_CB.addItem(field_name) + else: + # ensure the CB is not displaying stale information + self.dlg.ENV_riskLevelField_CB.clear() + self.dlg.ENV_riskLevelField_CB.setEnabled(False) + # Clear and disable the ENV_typeScore_Field by default + self.dlg.ENV_typeScore_Field.clear() + self.dlg.ENV_typeScore_Field.setEnabled(False) + + def populateTextFieldWithUniqueValues_PC_SAF(self, layer): + # Get the file path from the QgsFileWidget + file_path = self.dlg.SAF_Input_Field.filePath() + # Get the selected field name from the QComboBox + field_name = self.dlg.SAF_rasField_CB.currentText() + + # Clear and disable the SAF_typeScore_Field by default + self.dlg.SAF_typeScore_Field.clear() + self.dlg.SAF_typeScore_Field.setEnabled(False) + + if file_path and field_name: + if field_name.endswith(" (text)"): + # Remove " (text)" suffix + field_name = field_name.replace(" (text)", "") + try: + # Read the file using geopandas + gdf = gpd.read_file(file_path) + # Get unique values from the selected field + unique_values = gdf[field_name].unique().tolist() + # Create the score list + score_list = [[str(val), 0] for val in unique_values if pd.notna(val)] + # Set the text in the QFilterLineEdit and enable it + self.dlg.SAF_typeScore_Field.setText(str(score_list)) + self.dlg.SAF_typeScore_Field.setEnabled(True) + except Exception as e: + # Handle any errors (e.g., file not found, invalid field name) + self.dlg.SAF_typeScore_Field.setText(f"Error: {str(e)}") + self.dlg.SAF_typeScore_Field.setEnabled(False) + else: + # Field name doesn't end with " (text)", so we keep the field cleared and disabled + pass + else: + # Handle the case where no file or field is selected + self.dlg.SAF_typeScore_Field.setText("Please select a file and field first.") + self.dlg.SAF_typeScore_Field.setEnabled(False) + + def populateTextFieldWithUniqueValues_PC_ENV(self, layer): + # Get the file path from the QgsFileWidget + file_path = self.dlg.ENV_Input_Field.filePath() + # Get the selected field name from the QComboBox + field_name = self.dlg.ENV_riskLevelField_CB.currentText() + + # Clear and disable the ENV_typeScore_Field by default + self.dlg.ENV_typeScore_Field.clear() + self.dlg.ENV_typeScore_Field.setEnabled(False) + + if file_path and field_name: + if field_name.endswith(" (text)"): + # Remove " (text)" suffix + field_name = field_name.replace(" (text)", "") + try: + # Read the file using geopandas + gdf = gpd.read_file(file_path) + # Get unique values from the selected field + unique_values = gdf[field_name].unique().tolist() + # Create the score list + score_list = [[str(val), 0] for val in unique_values if pd.notna(val)] + # Set the text in the QFilterLineEdit and enable it + self.dlg.ENV_typeScore_Field.setText(str(score_list)) + self.dlg.ENV_typeScore_Field.setEnabled(True) + except Exception as e: + # Handle any errors (e.g., file not found, invalid field name) + self.dlg.ENV_typeScore_Field.setText(f"Error: {str(e)}") + self.dlg.ENV_typeScore_Field.setEnabled(False) + else: + # Field name doesn't end with " (text)", so we keep the field cleared and disabled + pass + else: + # Handle the case where no file or field is selected + self.dlg.ENV_typeScore_Field.setText("Please select a file and field first.") + self.dlg.ENV_typeScore_Field.setEnabled(False) + + def SAFnightTimeLights(self): + """ + This function processes night-time lights data for safety assessment. + It handles only raster inputs. How brightly lit an area is used as a proxy for safety or safe urban design. + """ + + # Set up directories + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + tempDir = "temp" + Dimension = "Place Characterization" + + # Create necessary directories + if not os.path.exists(Dimension): + os.mkdir(Dimension) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + time.sleep(0.5) + os.mkdir(tempDir) + + # Set CRS and input/output paths + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + input_file = self.dlg.SAF_Input_Field.filePath() + rasOutput = self.dlg.SAF_Output_Field.text() + + # Detect input file type, assuming it's raster + input_layer = QgsRasterLayer(input_file, "input") + + # Handle raster input layer + if input_layer.isValid(): + # Raster input detected - proceed with existing functionality + NTL_input = input_file + + # Define temporary file paths + tempCalc = f"{tempDir}/tempCalc.tif" + tempResample = f"{tempDir}/tempResample.tif" + countryUTMLayerBuf = f"{tempDir}/countryUTMLayerBuf.shp" + + # Copy style file + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + # Update UI status + self.dlg.SAF_status.setText("Variables Set") + self.dlg.SAF_status.repaint() + time.sleep(0.5) + self.dlg.SAF_status.setText("Processing...") + self.dlg.SAF_status.repaint() + + # Convert CRS of country layer + self.convertCRS(countryLayer, UTM_crs) + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + # Buffer the country layer + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": countryUTMLayerBuf, + }, + ) + + # Get the extent of the buffered country layer + CountryBuf_df = gpd.read_file(countryUTMLayerBuf) + country_extent = CountryBuf_df.total_bounds + + # Reproject and resample the night-time lights raster + processing.run( + "gdal:warpreproject", + { + "INPUT": NTL_input, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + # Perform raster calculation + processing.run( + "gdal:rastercalculator", + { + "INPUT_A": tempResample, + "BAND_A": 1, + "FORMULA": "A*1000", + "NO_DATA": None, + "RTYPE": 4, + "OPTIONS": "", + "EXTRA": "", + "OUTPUT": tempCalc, + }, + ) + + # Normalize raster values + with rasterio.open(tempCalc, "r+") as src: + SAF_ras = src.read(1) + meta1 = src.meta + Rmax = SAF_ras.max() + Rmin = SAF_ras.min() + m_max = 5 + m_min = 0 + result = ((SAF_ras - Rmin) / (Rmax - Rmin)) * m_max + meta1.update(dtype=rasterio.float32) + + try: + # Create output directory if it does not exist + if not os.path.exists(Dimension): + os.mkdir(Dimension) + os.chdir(Dimension) + + # Write the final output raster + with rasterio.open(rasOutput, "w", **meta1) as dst: + dst.write(result, 1) + + # Update UI with the output path + self.dlg.SAF_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + self.dlg.SAF_status.setText("Processing Complete!") + self.dlg.SAF_status.repaint() + + # Return to the original working directory + os.chdir(workingDir) + + except Exception as e: + # Something went awry, inform the user + error_message = f"Processing failed: {str(e)}" + self.dlg.SAF_status.setText(error_message) + self.dlg.SAF_status.repaint() + + # Ensure we return to the original working directory even if an error occurs + if os.getcwd() != workingDir: + os.chdir(workingDir) + + else: + self.dlg.SAF_status.setText("The input file is not a valid raster layer.") + self.dlg.SAF_status.repaint() + + # ------------------------------------ SAFETY factor rasterization section + def SAFCommonSetup(self): + """ + Common setup for SAF-related functions. Handles directory setup, CRS conversion, + and loading/buffering the country layer. + + Returns: + dict: A dictionary containing processed layers and paths. + """ + # Setup directories + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + if not workingDir.endswith('/'): + workingDir += '/' # Ensure workingDir ends with a trailing slash + Dimension = "Place Characterization" + tempDir = "temp" + os.makedirs(os.path.join(workingDir, Dimension), exist_ok=True) + os.makedirs(os.path.join(workingDir, tempDir), exist_ok=True) + os.chdir(workingDir) + + # Input parameters + countryLayerPath = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + UTM_crs = self.dlg.mQgsProjectionSelectionWidget.crs() + + # Load and preprocess the country layer + countryLayer = QgsVectorLayer(countryLayerPath, "country_layer", "ogr") + if not countryLayer.isValid(): + raise ValueError("Invalid country layer") + if countryLayer.crs() != UTM_crs: + countryLayer = processing.run("native:reprojectlayer", { + 'INPUT': countryLayer, + 'TARGET_CRS': UTM_crs, + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + # Buffer the country layer + buffered_country_layer_path = os.path.join(tempDir, "countryUTMLayerBuf.shp") + buffer = processing.run( + "native:buffer", + { + "INPUT": countryLayer, + "DISTANCE": self.BUFFER_DISTANCE, # Same buffer distance as in SAFnightTimeLights + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": buffered_country_layer_path, + }, + feedback=QgsProcessingFeedback() + ) + + buffered_country_layer = QgsVectorLayer(buffered_country_layer_path, "buffered_country_layer", "ogr") + if not buffered_country_layer.isValid(): + raise ValueError("Buffering failed. Invalid buffered layer.") + + # Get the extent of the buffered country layer + country_extent = buffered_country_layer.extent() + + return { + "current_script_path": current_script_path, + "workingDir": workingDir, + "Dimension": Dimension, + "tempDir": tempDir, + "countryLayer": countryLayer, + "buffered_country_layer": buffered_country_layer, + "country_extent": country_extent, + "pixelSize": pixelSize, + "UTM_crs": UTM_crs + } + + def SAFstreetLightsRasterizer(self): + """ + Process streetlight vector data for safety assessment. + Count streetlight points within a grid, scale to 0-5 range, and rasterize using rasterio. + """ + try: + # Call the common setup function + setup = self.SAFCommonSetup() + + # Input parameters + input_file = self.dlg.SAF_Input_Field.filePath() + + self.dlg.SAF_status.setText("Variables Set") + self.dlg.SAF_status.repaint() + time.sleep(0.5) + self.dlg.SAF_status.setText("Processing...") + self.dlg.SAF_status.repaint() + + # Load and preprocess vector layer + vector_layer = QgsVectorLayer(input_file, "streetlights", "ogr") + if not vector_layer.isValid(): + raise ValueError("Invalid vector input. Please check the file.") + if vector_layer.crs() != setup['UTM_crs']: + vector_layer = processing.run("native:reprojectlayer", { + 'INPUT': vector_layer, + 'TARGET_CRS': setup['UTM_crs'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + # Generate grid and count points + grid = processing.run("native:creategrid", { + 'TYPE': 2, + 'EXTENT': setup['country_extent'], + 'HSPACING': setup['pixelSize'], + 'VSPACING': setup['pixelSize'], + 'CRS': setup['UTM_crs'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + clipped_grid = processing.run("native:clip", { + 'INPUT': grid, + 'OVERLAY': setup['buffered_country_layer'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + overlap_count = processing.run("qgis:countpointsinpolygon", { + 'POLYGONS': clipped_grid, + 'POINTS': vector_layer, + 'FIELD': 'OVERLAP', + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + # Prepare raster data + xmin, ymin, xmax, ymax = setup['country_extent'].toRectF().getCoords() + width = int(np.floor((xmax - xmin) / setup['pixelSize'])) + height = int(np.floor((ymax - ymin) / setup['pixelSize'])) + raster_data = np.zeros((height, width), dtype=np.float32) + + # Rasterize vector data + for feature in overlap_count.getFeatures(): + geom = feature.geometry() + if geom.type() == QgsWkbTypes.PolygonGeometry: + centroid = geom.centroid().asPoint() + col = int((centroid.x() - xmin) / setup['pixelSize']) + row = int((ymax - centroid.y()) / setup['pixelSize']) + if 0 <= row < height and 0 <= col < width: + raster_data[row, col] = feature['OVERLAP'] + + # Normalize raster data + max_value = np.max(raster_data) + if max_value > 0: + raster_data = (raster_data / max_value) * 5.0 + + # Create output raster + raster_output = os.path.join(setup['workingDir'], setup['Dimension'], self.dlg.SAF_Output_Field.text()) + transform = rasterio.transform.from_origin(xmin, ymax, setup['pixelSize'], setup['pixelSize']) + + with rasterio.open( + raster_output, + 'w', + driver='GTiff', + height=height, + width=width, + count=1, + dtype=raster_data.dtype, + crs=setup['UTM_crs'].toWkt(), + transform=transform, + nodata=0 # Explicitly setting NoData value + ) as dst: + dst.write(raster_data, 1) + dst.nodata = 0.0 + + # Set output field and apply style + self.dlg.SAF_Aggregate_Field.setText(raster_output) + styleTemplate = os.path.join(setup['current_script_path'], "Style", f"{setup['Dimension']}.qml") + styleFile = f"{os.path.splitext(os.path.basename(raster_output))[0]}.qml" + shutil.copy(styleTemplate, os.path.join(setup['workingDir'], setup['Dimension'], styleFile)) + + self.dlg.SAF_status.setText("Processing has been completed!") + self.dlg.SAF_status.repaint() + + except Exception as e: + self.dlg.SAF_status.setText(f"Error: {str(e)}") + self.dlg.SAF_status.repaint() + + def SAFPerceivedSafetyFromUserValueRasterizer(self, user_value): + """ + This function rasterizes the Perceived Safety factor for the Urban Safety Factor based on a user-provided value. + It creates a raster with the user-provided value within the country bounds and fills pixels outside the country shape with zero. + The value provided by the user is in the range 0-100, but the value to render the output at is 0-5, + so it's user value divided by 20.0. + """ + try: + # Call the common setup function + setup = self.SAFCommonSetup() + + # Convert the user-provided value to the 0-5 scale + render_value = user_value / 20.0 + + self.dlg.SAF_status.setText("Variables Set") + self.dlg.SAF_status.repaint() + time.sleep(0.5) + self.dlg.SAF_status.setText("Processing...") + self.dlg.SAF_status.repaint() + + # Get the extent of the buffered country layer + xmin, ymin, xmax, ymax = setup['country_extent'].toRectF().getCoords() + + # Initialize raster data with zeros + width = int(np.floor((xmax - xmin) / setup['pixelSize'])) + height = int(np.floor((ymax - ymin) / setup['pixelSize'])) + raster_data = np.zeros((height, width), dtype=np.float32) + + # Create the transform for the raster + transform = rasterio.transform.from_origin(xmin, ymax, setup['pixelSize'], setup['pixelSize']) + + # Write initial raster to a temporary file with zeros (NoData value) + temp_raster_path = os.path.join(setup['tempDir'], "temp_raster.tif") + with rasterio.open( + temp_raster_path, 'w', driver='GTiff', height=height, width=width, + count=1, dtype=np.float32, crs=setup['UTM_crs'].toWkt(), transform=transform, nodata=0 + ) as dst: + dst.write(raster_data, 1) + + # Rasterize the user value within the country bounds + processing.run( + "gdal:rasterize", + { + "INPUT": setup['countryLayer'], + "FIELD": "", # No field since we use a uniform value + "BURN": render_value, # Use the scaled user-provided value + "USE_Z": False, + "UNITS": 1, + "WIDTH": setup['pixelSize'], + "HEIGHT": setup['pixelSize'], + "EXTENT": f"{xmin},{xmax},{ymin},{ymax}", + "NODATA": 0, # NoData is zero outside the country bounds + "OPTIONS": "", + "DATA_TYPE": 6, # GDT_Float32 for real numbers + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": temp_raster_path # Output to the temporary raster + } + ) + + # Copy the temporary raster to the final output location + raster_output = os.path.join(setup['workingDir'], setup['Dimension'], self.dlg.SAF_Output_Field.text()) + shutil.move(temp_raster_path, raster_output) + + # Set output field and apply style + self.dlg.SAF_Aggregate_Field.setText(raster_output) + styleTemplate = os.path.join(setup['current_script_path'], "Style", f"{setup['Dimension']}.qml") + styleFile = f"{os.path.splitext(os.path.basename(raster_output))[0]}.qml" + shutil.copy(styleTemplate, os.path.join(setup['workingDir'], setup['Dimension'], styleFile)) + + self.dlg.SAF_status.setText("Processing has been completed!") + self.dlg.SAF_status.repaint() + + except Exception as e: + self.dlg.SAF_status.setText(f"Error: {str(e)}") + self.dlg.SAF_status.repaint() + + def SAFPerceivedSafetyFromTextFieldRasterizer(self, layer): + """ + This function rasterizes the Perceived Safety factor for the Urban Safety Factor. + It converts vector data to raster, applying scores based on text attributes. + """ + try: + # Call the common setup function + setup = self.SAFCommonSetup() + + # Get the field name + rasField = self.dlg.SAF_rasField_CB.currentText().replace(" (text)", "") + + # Get scoring from SAF_typeScore_Field + scoring_text = self.dlg.SAF_typeScore_Field.text() + scoring_dict = {item[0]: item[1] for item in eval(scoring_text)} + + self.dlg.SAF_status.setText("Variables Set") + self.dlg.SAF_status.repaint() + time.sleep(0.5) + self.dlg.SAF_status.setText("Processing...") + self.dlg.SAF_status.repaint() + + # Ensure the layer is valid + if not layer.isValid(): + raise ValueError("Invalid layer") + + # Convert CRS if necessary + if layer.crs() != setup['UTM_crs']: + layer = processing.run("native:reprojectlayer", { + 'INPUT': layer, + 'TARGET_CRS': setup['UTM_crs'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + # Clip the input layer by the country layer + clipped_layer = processing.run("native:clip", { + 'INPUT': layer, + 'OVERLAY': setup['countryLayer'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + if not any(clipped_layer.getFeatures()): + raise ValueError("Clipping resulted in an empty layer") + + # Create a temporary memory layer to hold the scaled scores + temp_layer = QgsVectorLayer("Polygon?crs=" + setup['UTM_crs'].authid(), "temp_layer", "memory") + temp_layer.dataProvider().addAttributes( + [QgsField("scaled_score", QVariant.Double)]) # Use Double for real numbers + temp_layer.updateFields() + + # Calculate the scaled scores and add to temp layer + temp_layer.startEditing() + for feature in clipped_layer.getFeatures(): + value = feature[rasField] + score = scoring_dict.get(value, 0.0) # Default to 0.0 if value is not in scoring_dict + new_feature = QgsFeature(temp_layer.fields()) + new_feature.setGeometry(feature.geometry()) + new_feature.setAttribute("scaled_score", float(score)) + temp_layer.addFeature(new_feature) + temp_layer.commitChanges() + + # Get the extent for rasterization + extent = temp_layer.extent() + xmin, ymin, xmax, ymax = extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() + + # Rasterize + rasOutput = os.path.join(setup['workingDir'], setup['Dimension'], self.dlg.SAF_Output_Field.text()) + _ = processing.run( + "gdal:rasterize", + { + "INPUT": temp_layer, + "FIELD": "scaled_score", + "BURN": 0.0, + "USE_Z": False, + "UNITS": 1, + "WIDTH": setup['pixelSize'], + "HEIGHT": setup['pixelSize'], + "EXTENT": f"{xmin},{xmax},{ymin},{ymax}", + "NODATA": 0, # Ensure NoData is set to 0 + "OPTIONS": "", + "DATA_TYPE": 6, # GDT_Float32 for real numbers + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput + } + ) + + # Set output field and apply style + self.dlg.SAF_Aggregate_Field.setText(rasOutput) + styleTemplate = os.path.join(setup['current_script_path'], "Style", f"{setup['Dimension']}.qml") + styleFileDestination = os.path.join(setup['workingDir'], setup['Dimension']) + styleFile = f"{os.path.splitext(os.path.basename(rasOutput))[0]}.qml" + shutil.copy(styleTemplate, styleFileDestination, styleFile) + + # Update status + self.dlg.SAF_status.setText("Processing has been completed!") + self.dlg.SAF_status.repaint() + + except Exception as e: + self.dlg.SAF_status.setText(f"Error: {str(e)}") + self.dlg.SAF_status.repaint() + + def SAFPerceivedSafetyFromNumericFieldRasterizer(self, layer): + """ + This function rasterizes the Perceived Safety factor for the Urban Safety Factor. + It converts vector data to raster, applying necessary transformations and standardizations using a scaling approach. + """ + try: + # Call the common setup function + setup = self.SAFCommonSetup() + + # Get the field name + rasField = self.dlg.SAF_rasField_CB.currentText() + + self.dlg.SAF_status.setText("Variables Set") + self.dlg.SAF_status.repaint() + time.sleep(0.5) + self.dlg.SAF_status.setText("Processing...") + self.dlg.SAF_status.repaint() + + # Ensure the layer is valid + if not layer.isValid(): + raise ValueError("Invalid layer") + + # Convert CRS if necessary + if layer.crs() != setup['UTM_crs']: + layer = processing.run("native:reprojectlayer", { + 'INPUT': layer, + 'TARGET_CRS': setup['UTM_crs'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + # Clip the input layer by the country layer + clipped_layer = processing.run("native:clip", { + 'INPUT': layer, + 'OVERLAY': setup['countryLayer'], + 'OUTPUT': 'memory:' + }, feedback=QgsProcessingFeedback())['OUTPUT'] + + if not any(clipped_layer.getFeatures()): + raise ValueError("Clipping resulted in an empty layer") + + # Get min and max values of the field + values = [f[rasField] for f in clipped_layer.getFeatures() if f[rasField] is not None] + if not values: + raise ValueError("No valid values in the selected field") + min_val, max_val = min(values), max(values) + + # Scale values to the 0-5 range + def scale_value(val): + return 0.0 if max_val == min_val else (val - min_val) / (max_val - min_val) * 5.0 + + # Create a temporary memory layer to hold the scaled scores + temp_layer = QgsVectorLayer("Polygon?crs=" + setup['UTM_crs'].authid(), "temp_layer", "memory") + temp_layer.dataProvider().addAttributes( + [QgsField("scaled_score", QVariant.Double)]) # Use Double for real numbers + temp_layer.updateFields() + + # Calculate the scaled scores and add to temp layer + temp_layer.startEditing() + for feature in clipped_layer.getFeatures(): + value = feature[rasField] + score = scale_value(value) + new_feature = QgsFeature(temp_layer.fields()) + new_feature.setGeometry(feature.geometry()) + new_feature.setAttribute("scaled_score", score) + temp_layer.addFeature(new_feature) + temp_layer.commitChanges() + + # Get the extent for rasterization + extent = temp_layer.extent() + xmin, ymin, xmax, ymax = extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() + + # Rasterize + rasOutput = os.path.join(setup['workingDir'], setup['Dimension'], self.dlg.SAF_Output_Field.text()) + _ = processing.run( + "gdal:rasterize", + { + "INPUT": temp_layer, + "FIELD": "scaled_score", + "BURN": 0.0, + "USE_Z": False, + "UNITS": 1, + "WIDTH": setup['pixelSize'], + "HEIGHT": setup['pixelSize'], + "EXTENT": f"{xmin},{xmax},{ymin},{ymax}", + "NODATA": 0, # Ensure NoData is set to 0 + "OPTIONS": "", + "DATA_TYPE": 6, # GDT_Float32 for real numbers + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput + } + ) + + # Set output field and apply style + self.dlg.SAF_Aggregate_Field.setText(rasOutput) + styleTemplate = os.path.join(setup['current_script_path'], "Style", f"{setup['Dimension']}.qml") + styleFileDestination = os.path.join(setup['workingDir'], setup['Dimension']) + styleFile = f"{os.path.splitext(os.path.basename(rasOutput))[0]}.qml" + shutil.copy(styleTemplate, styleFileDestination, styleFile) + + # Update status + self.dlg.SAF_status.setText("Processing has been completed!") + self.dlg.SAF_status.repaint() + + except Exception as e: + self.dlg.SAF_status.setText(f"Error: {str(e)}") + self.dlg.SAF_status.repaint() + + def EDUShapefileOrUserInputRasterizer(self): + """ + This function rasterizes the Education factor for the Urban Safety Factor. + It either uses an incoming shapefile and the field corresponding to the combobox, + or a user-specified value to rasterize. + """ + + # Update status + self.dlg.EDU_status.setText("Starting...") + self.dlg.EDU_status.repaint() + + try: + # Set up variables + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + countryLayerPath = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + UTM_crs = self.dlg.mQgsProjectionSelectionWidget.crs() + Dimension = "Place Characterization" + tempDir = os.path.join(workingDir, "temp") + + # Create necessary directories + if not os.path.exists(workingDir): + os.mkdir(workingDir) + + dimension_dir = os.path.join(workingDir, Dimension) + if not os.path.exists(dimension_dir): + os.mkdir(dimension_dir) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + time.sleep(0.5) + os.mkdir(tempDir) + + # Copy style file + styleTemplate = os.path.join(current_script_path, "Style", f"{Dimension}.qml") + styleFileDestination = dimension_dir + styleFile = f"{os.path.splitext(self.dlg.EDU_Output_Field.text())[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + # Load country layer + countryLayer = QgsVectorLayer(countryLayerPath, "country_layer", "ogr") + if not countryLayer.isValid(): + raise ValueError("Invalid country layer") + + # Ensure spatial index exists + _ = QgsSpatialIndex(countryLayer.getFeatures()) + + # Check if the QComboBox has a selected value + rasField = self.dlg.EDU_rasField_CB.currentText() + if rasField: + # Update status + self.dlg.EDU_status.setText("Processing polygon layer...") + self.dlg.EDU_status.repaint() + + # Use the incoming shapefile and the field corresponding to the checkbox + polygonLayerPath = self.dlg.EDU_Input_Field.filePath() + polygonLayer = QgsVectorLayer(polygonLayerPath, "polygon_layer", "ogr") + if not polygonLayer.isValid(): + raise ValueError("Invalid polygon layer") + + if polygonLayer.crs() != UTM_crs: + polygonLayer = processing.run("native:reprojectlayer", { + 'INPUT': polygonLayer, + 'TARGET_CRS': UTM_crs, + 'OUTPUT': 'memory:' + })['OUTPUT'] + + # Clip the polygon layer by the country layer + clipped_layer = processing.run("native:clip", { + 'INPUT': polygonLayer, + 'OVERLAY': countryLayer, + 'OUTPUT': 'memory:' + })['OUTPUT'] + + if not any(clipped_layer.getFeatures()): + raise ValueError("Clipping resulted in an empty layer") + + # Get min and max values of the field + values = [f[rasField] for f in clipped_layer.getFeatures() if f[rasField] is not None] + if not values: + raise ValueError("No valid values in the selected field") + min_val, max_val = min(values), max(values) + + # Scale values to 0-5 range + def scale_value(val): + return 0.0 if max_val == min_val else (val - min_val) / (max_val - min_val) * 5.0 + + # Create a temporary memory layer to hold the scaled scores + temp_layer = QgsVectorLayer("Polygon?crs=" + UTM_crs.authid(), "temp_layer", "memory") + temp_layer.dataProvider().addAttributes( + [QgsField("scaled_score", QVariant.Double)]) # Use Double for real numbers + temp_layer.updateFields() + + # Calculate the scaled scores and add to temp layer + temp_layer.startEditing() + for feature in clipped_layer.getFeatures(): + value = feature[rasField] + score = scale_value(value) + new_feature = QgsFeature(temp_layer.fields()) + new_feature.setGeometry(feature.geometry()) + new_feature.setAttribute("scaled_score", score) + temp_layer.addFeature(new_feature) + + temp_layer.commitChanges() + + else: + # Use the user-specified value to rasterize the country layer + + # Update status + self.dlg.EDU_status.setText("Processing user value...") + self.dlg.EDU_status.repaint() + + user_value = self.dlg.EDU_User_Value_Input.value() + temp_layer = QgsVectorLayer("Polygon?crs=" + UTM_crs.authid(), "temp_layer", "memory") + temp_layer.dataProvider().addAttributes( + [QgsField("scaled_score", QVariant.Double)]) # Use Double for real numbers + temp_layer.updateFields() + + temp_layer.startEditing() + for feature in countryLayer.getFeatures(): + new_feature = QgsFeature(temp_layer.fields()) + new_feature.setGeometry(feature.geometry()) + new_feature.setAttribute("scaled_score", + user_value / 20.0) # Normalize to a 0-5 scale as a real number + temp_layer.addFeature(new_feature) + + temp_layer.commitChanges() + + # Ensure spatial index for temp_layer + _ = QgsSpatialIndex(temp_layer.getFeatures()) + + # Get the extent for rasterization + UTM_crs_2 = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(":")[-1][:-1] + self.convertCRS(countryLayerPath, UTM_crs_2) + shp_utm[rasField] = [0] + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + countryUTMLayerBuf = buffer["OUTPUT"] + extent = countryUTMLayerBuf.extent() + xmin, ymin, xmax, ymax = extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() + + rasOutput = self.dlg.EDU_Output_Field.text() + temp_rasOutputPath = os.path.join(tempDir, "temp_raster.tif") + final_rasOutputPath = os.path.join(workingDir, Dimension, rasOutput) + + # Update status + self.dlg.EDU_status.setText("Rasterizing...") + self.dlg.EDU_status.repaint() + + # Rasterize using the pixelSize for resolution + _ = processing.run( + "gdal:rasterize", + { + "INPUT": temp_layer, + "FIELD": "scaled_score", + "BURN": 0, + "USE_Z": False, + "UNITS": 1, + "WIDTH": pixelSize, + "HEIGHT": pixelSize, + "EXTENT": f"{xmin},{xmax},{ymin},{ymax}", + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 6, # GDT_Float32 for real numbers + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": temp_rasOutputPath # Output to a temporary file for clipping + } + ) + + # Update status + self.dlg.EDU_status.setText("Clipping raster...") + self.dlg.EDU_status.repaint() + + # Clip the raster to the country layer + _ = processing.run( + "gdal:cliprasterbymasklayer", + { + "INPUT": temp_rasOutputPath, + "MASK": countryLayer, + "SOURCE_CRS": UTM_crs, + "TARGET_CRS": UTM_crs, + "NODATA": None, + "ALPHA_BAND": False, + "CROP_TO_CUTLINE": False, + "KEEP_RESOLUTION": True, + "OPTIONS": "", + "DATA_TYPE": 6, # GDT_Float32 for real numbers + "OUTPUT": final_rasOutputPath + } + ) + + # Set output field + self.dlg.EDU_Aggregate_Field.setText(final_rasOutputPath) + + # Update status + self.dlg.EDU_status.setText("Processing has been completed!") + self.dlg.EDU_status.repaint() + + except Exception as e: + print(str(e)) + self.dlg.EDU_status.setText(f"Error: {str(e)}") + self.dlg.EDU_status.repaint() + + + def ELCnightTimeLights(self): + """ + This function use a linearly scales the night time lights raster dataset according to the standardized scoring system. + How brightly lit an area is is used as a proxy ro electricty access. + + Factors it is applied: + Place Characterization Dimension + - Electrical Access + """ + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + tempDir = "temp" + Dimension = "Place Characterization" + + if os.path.exists(Dimension): + pass + else: + os.mkdir(Dimension) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + NTL_input = self.dlg.ELC_NTLInput_Field.filePath() + rasOutput = self.dlg.ELC_NTLOutput_Field.text() + + # Temp files + tempCalc = f"{tempDir}/tempCalc.tif" + tempResample = f"{tempDir}/tempResample.tif" + countryUTMLayerBuf = f"{tempDir}/countryUTMLayerBuf.shp" + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ELCNTL_status.setText("Variables Set") + self.dlg.ELCNTL_status.repaint() + time.sleep(0.5) + self.dlg.ELCNTL_status.setText("Processing...") + self.dlg.ELCNTL_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": countryUTMLayerBuf, + }, + ) + + # countryUTMLayerBuf = buffer["OUTPUT"] + + CountryBuf_df = gpd.read_file(countryUTMLayerBuf) + country_extent = CountryBuf_df.total_bounds + + processing.run( + "gdal:warpreproject", + { + "INPUT": NTL_input, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + processing.run( + "gdal:rastercalculator", + { + "INPUT_A": tempResample, + "BAND_A": 1, + "INPUT_B": None, + "BAND_B": None, + "INPUT_C": None, + "BAND_C": None, + "INPUT_D": None, + "BAND_D": None, + "INPUT_E": None, + "BAND_E": None, + "INPUT_F": None, + "BAND_F": None, + "FORMULA": "A*1000", + "NO_DATA": None, + "EXTENT_OPT": 0, + "PROJWIN": None, + "RTYPE": 4, + "OPTIONS": "", + "EXTRA": "", + "OUTPUT": tempCalc, + }, + ) + + with rasterio.open(tempCalc, "r+") as src: + ELC_ras = src.read(1) + meta1 = src.meta + + Rmax = ELC_ras.max() + Rmin = ELC_ras.min() + m_max = 5 + m_min = 0 + + result = ((ELC_ras - Rmin) / (Rmax - Rmin)) * m_max + meta1.update(dtype=rasterio.float32) + + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + with rasterio.open(rasOutput, "w", **meta1) as dst: + dst.write(result, 1) + + self.dlg.ELC_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + self.dlg.ELCNTL_status.setText("Processing Complete!") + self.dlg.ELCNTL_status.repaint() + + os.chdir(workingDir) + + def urbanization(self): + """ + This algorithm characterizes areas based on their degree of urbanization. The algorithm reclassifies the eight + classes in the input data into classes ranging from 0 to 5 as per the standardized scoring system. + + Factors it is applied: + Place Characterization Dimension + - Level of Urbanization + """ + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + tempDir = "temp" + Dimension = "Place Characterization" + + if os.path.exists(Dimension): + pass + else: + os.mkdir(Dimension) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + + LOU_input = self.dlg.LOU_Input_Field.filePath() + rasOutput = self.dlg.LOU_Output_Field.text() + rasField = "Score" + + # Temp Files + tempRas = f"{tempDir}/RasReproj.tif" + tempVect = f"{tempDir}/tempVect.shp" + Dissolve = f"{tempDir}/Dissolve.shp" + DisReclass = f"{tempDir}/DisReclass.shp" + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.LOU_status.setText("Variables Set") + self.dlg.LOU_status.repaint() + time.sleep(0.5) + self.dlg.LOU_status.setText("Processing...") + self.dlg.LOU_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + shp_utm[rasField] = [0] + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + countryUTMLayerBuf = buffer["OUTPUT"] + + Reproject = processing.run( + "gdal:warpreproject", + { + "INPUT": LOU_input, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": None, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": None, + "TARGET_EXTENT_CRS": None, + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempRas, + }, + ) + + vector = processing.run( + "gdal:polygonize", + { + "INPUT": tempRas, + "BAND": 1, + "FIELD": "DN", + "EIGHT_CONNECTEDNESS": False, + "EXTRA": "", + "OUTPUT": tempVect, + }, + ) + + time.sleep(0.5) + + dissolve = processing.run( + "native:dissolve", + { + "INPUT": tempVect, + "FIELD": ["DN"], + "SEPARATE_DISJOINT": False, + "OUTPUT": Dissolve, + }, + ) + + time.sleep(0.5) + + GHS_df = gpd.read_file(Dissolve) + GHS_df["Score"] = 0 + GHS_df["Score"] = GHS_df["Score"].astype(int) + + conditions = [ + (GHS_df["DN"] == 30), + (GHS_df["DN"] == 23), + (GHS_df["DN"] == 22), + (GHS_df["DN"] == 21), + (GHS_df["DN"] == 13), + (GHS_df["DN"] == 12), + (GHS_df["DN"] == 11), + (GHS_df["DN"] == 10), + ] + + values = [5, 4, 3, 3, 2, 1, 1, 0] + GHS_df["Score"] = np.select(conditions, values) + + GHS_vec = QgsVectorLayer(GHS_df.to_json(), "GHS_vec", "ogr") + + Clip = processing.run( + "native:clip", + {"INPUT": GHS_vec, "OVERLAY": countryUTMLayerBuf, "OUTPUT": "memory:"}, + ) + + Clip_out = Clip["OUTPUT"] + + extent = Clip_out.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + os.chdir(Dimension) + rasOutput = self.dlg.LOU_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": Clip_out, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.LOU_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + self.dlg.LOU_status.setText("Processing Complete!") + self.dlg.LOU_status.repaint() + + def housing(self): + """ + This function characterizes areas based on the size of housing and assumes that buildings with a footprint of less than 60 m2 are more likely to represent informal housing typologies. + algorithm calculates the area of each building footprint in the input layer. It then overlays the input layer onto a hexagonal grid and calculates the percentage + of buildings within each hexagonal cell with a footprint greater than 60 m2. + + The output produced is a raster file containing values ranging from 0 to 5 is saved to the output directory. In this scale, + 5 signifies areas where 100% of buildings have a footprint larger than 60 m2 (and can be interpreted as entirely formal settlements), + while 0 indicates areas where no buildings possess a footprint larger than 60 m2 (and can be interpreted as entirely informal settlements). + + Factors it is applied: + Place Characterization Dimension + - Size of Housing + """ + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + tempDir = "temp" + Dimension = "Place Characterization" + + if os.path.exists(Dimension): + pass + else: + os.mkdir(Dimension) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + hexSize = self.dlg.QUH_hexSize_SB.value() + buildingFootprints = self.dlg.QUH_Input_Field.filePath() + rasField = "Perc" + + # TempFiles + scoredRoads = f"{workingDir}/{tempDir}/Scored_roads.shp" + adminUTMLayer = f"{tempDir}/adminUTMLayer.shp" + buildingOutput = f"{tempDir}/buildingOutput.shp" + hexPercOutput = f"{tempDir}/hexPercOutput.shp" + hexRasOutput = f"{tempDir}/hexRasOutput.tif" + zonalOutput = f"{tempDir}/zonalOutput.shp" + buildingFootprintsUTM = f"{tempDir}/buildingFootprintsUTM.shp" + + self.dlg.QUH_status.setText("Variables Set") + self.dlg.QUH_status.repaint() + time.sleep(0.5) + self.dlg.QUH_status.setText("Processing...") + self.dlg.QUH_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + shp_utm[rasField] = [0] + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": "memory:", + }, + ) + + countryUTMLayerBuf = buffer["OUTPUT"] + + country_extent = shp_utm.total_bounds + + Grid = processing.run( + "native:creategrid", + { + "TYPE": 4, + "EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "HSPACING": hexSize, + "VSPACING": hexSize, + "HOVERLAY": 0, + "VOVERLAY": 0, + "CRS": QgsCoordinateReferenceSystem(f"{UTM_crs}"), + "OUTPUT": "memory:", + }, + ) + grid_out = Grid["OUTPUT"] + + Clip = processing.run( + "native:clip", + {"INPUT": grid_out, "OVERLAY": countryUTMLayer, "OUTPUT": "memory:"}, + ) + + clip_out = Clip["OUTPUT"] + + # Rasterize hexegons + extent = clip_out.extent() + raster_width = int(extent.width() / (pixelSize / 10)) + raster_height = int(extent.height() / (pixelSize)) + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": clip_out, + "FIELD": "id", + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": hexRasOutput, + }, + ) + + self.convertCRS(buildingFootprints, UTM_crs) + shp_utm.to_file(buildingFootprintsUTM) + + # Zonal Statistics using majority + + zonal = processing.run( + "native:zonalstatisticsfb", + { + "INPUT": QgsProcessingFeatureSourceDefinition( + buildingFootprintsUTM, + selectedFeaturesOnly=False, + featureLimit=-1, + flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck, + geometryCheck=QgsFeatureRequest.GeometrySkipInvalid, + ), + "INPUT_RASTER": hexRasOutput, + "RASTER_BAND": 1, + "COLUMN_PREFIX": "_", + "STATISTICS": [9], + "OUTPUT": zonalOutput, + }, + ) + + Clip = processing.run( + "native:clip", + {"INPUT": grid_out, "OVERLAY": countryUTMLayer, "OUTPUT": adminUTMLayer}, + ) + + BF_gdf = gpd.read_file(zonalOutput) + Admin_gdf = gpd.read_file(adminUTMLayer) + BF_gdf = gpd.overlay(Admin_gdf, BF_gdf, how="intersection") + + building_geometries = BF_gdf["geometry"] + BF_gdf["Area"] = building_geometries.area + BF_gdf["more_60"] = 0 + BF_gdf.loc[BF_gdf["Area"] > 60, "more_60"] = 1 + BF_gdf["id"] = BF_gdf["_majority"].astype(int) + BF_gdf.to_file(buildingOutput) + + grouped = BF_gdf.groupby("id").size().reset_index(name="Total_Count") + + filtered_df = BF_gdf[BF_gdf["more_60"] == 1] + + grouped2 = filtered_df.groupby("id").size().reset_index(name="More60_Count") + + merged = pd.merge(grouped, grouped2, on=["id"], how="outer") + merged_length_gdf = pd.DataFrame(merged) + merged_length_gdf[rasField] = ( + merged_length_gdf["More60_Count"] / merged_length_gdf["Total_Count"] * 100 + ) + + hex_gdf = gpd.read_file(adminUTMLayer) + merge_hex_gdf = hex_gdf.merge(merged_length_gdf, on="id", how="outer") + # merge_hex_gdf.to_file(FinalHexOutput) + + # Rasterization + + Rmax = 100 + Rmin = 0 + m_max = 5 + m_min = 0 + + merge_hex_gdf[rasField] = ( + (merge_hex_gdf[rasField] - Rmin) / (Rmax - Rmin) * m_max + ) + merge_hex_gdf.to_file(hexPercOutput) + + Difference = processing.run( + "native:difference", + { + "INPUT": countryUTMLayerBuf, + "OVERLAY": hexPercOutput, + "OUTPUT": "memory:", + "GRID_SIZE": None, + }, + ) + + difference = Difference["OUTPUT"] + + diff = processing.run( + "native:polygonstolines", + { + "INPUT": difference, + "OUTPUT": "memory:" + } + ) + + difference = diff["OUTPUT"] + + Merge = processing.run( + "native:mergevectorlayers", + {"LAYERS": [hexPercOutput, difference], "CRS": None, "OUTPUT": "memory:"}, + ) + + mergeOutput = Merge["OUTPUT"] + + extent = mergeOutput.extent() + raster_width = int(extent.width() / pixelSize) + raster_height = int(extent.height() / pixelSize) + + os.chdir(Dimension) + + rasOutput = self.dlg.QUH_Output_Field.text() + + rasterize = processing.run( + "gdal:rasterize", + { + "INPUT": mergeOutput, + "FIELD": rasField, + "BURN": 0, + "USE_Z": False, + "UNITS": 0, + "WIDTH": raster_width, + "HEIGHT": raster_height, + "EXTENT": None, + "NODATA": None, + "OPTIONS": "", + "DATA_TYPE": 5, + "INIT": None, + "INVERT": False, + "EXTRA": "", + "OUTPUT": rasOutput, + }, + ) + + self.dlg.QUH_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.QUH_status.setText("Processing Complete!") + self.dlg.QUH_status.repaint() + + def natEnvironment(self): + """ + This function is used in combination with the "TypeSet" and "uniqueValues" functions to execute the Natural Environment and Climatic factors algorithm. + The input layers are rasterized according to the scoring assigned to the unique hazard values. + + Factors it is applied: + Place Characterization Dimension + - Environmental Hazards + """ + try: + # Set up variables + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + Dimension = "Place Characterization" + tempDir = os.path.join(workingDir, "temp") + + if not os.path.exists(workingDir): + os.mkdir(workingDir) + + dimension_dir = os.path.join(workingDir, Dimension) + if not os.path.exists(dimension_dir): + os.mkdir(dimension_dir) + + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + time.sleep(0.5) + os.mkdir(tempDir) + + # Temp files + tempCalc = f"{tempDir}/tempEnvCalc.tif" + tempResample = f"{tempDir}/tempEnvResample.tif" + tempClipResample = f"{tempDir}/tempEnvClipResample.tif" + countryUTMLayerBuf = f"{tempDir}/countryUTMLayerBuf.shp" + + + UTM_crs = self.dlg.mQgsProjectionSelectionWidget.crs() + countryLayerPath = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + fireHazardLayer = self.dlg.ENV_Input_Field.filePath() + floodLayer = self.dlg.ENV_Input_Field_2.filePath() + landSlideLayer = self.dlg.ENV_Input_Field_3.filePath() + cycloneLayer = self.dlg.ENV_Input_Field_4.filePath() + droughtLayer = self.dlg.ENV_Input_Field_5.filePath() + #riskLevelField = self.dlg.ENV_riskLevelField_CB.currentText() + + rasField = "Score" + + self.dlg.ENV_status.setText("Variables Set") + self.dlg.ENV_status.repaint() + time.sleep(0.5) + self.dlg.ENV_status.setText("Processing...") + self.dlg.ENV_status.repaint() + + # Load and reproject country layer if necessary + countryLayer = QgsVectorLayer(countryLayerPath, "country_layer", "ogr") + if not countryLayer.isValid(): + raise ValueError("Invalid country layer") + if countryLayer.crs() != UTM_crs: + countryLayer = processing.run("native:reprojectlayer", { + 'INPUT': countryLayer, + 'TARGET_CRS': UTM_crs, + 'OUTPUT': 'memory:' + })['OUTPUT'] + + # Ensure spatial index exists + _ = QgsSpatialIndex(countryLayer.getFeatures()) + #_ = QgsSpatialIndex(polygonLayer.getFeatures()) + + # Buffer the country layer + buffer = processing.run( + "native:buffer", + { + "INPUT": countryLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": countryUTMLayerBuf, + }, + ) + countryUTMLayerBuf = buffer["OUTPUT"] + + CountryBuf_df = gpd.read_file(countryUTMLayerBuf) + country_extent = CountryBuf_df.total_bounds + + if fireHazardLayer: + processing.run( + "gdal:warpreproject", + { + "INPUT": fireHazardLayer, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + with rasterio.open(tempResample, "r+") as src: + ENV_ras = src.read(1) + meta1 = src.meta + + #Rmax = ENV_ras.max() + #Rmin = ENV_ras.min() + #m_max = 5 + #m_min = 0 + + #result = ((ENV_ras - Rmin) / (Rmax - Rmin)) * m_max + reclassified_ras = np.vectorize(self.reclassifyFireHazards)(ENV_ras) + meta1.update(dtype=rasterio.float32) + + if os.path.exists(f"{workingDir}/{Dimension}/ENV"): + os.chdir(f"{workingDir}/{Dimension}/ENV") + else: + os.mkdir(f"{workingDir}/{Dimension}/ENV") + os.chdir(f"{workingDir}/{Dimension}/ENV") + + rasOutput = f"{self.dlg.ENV_Output_Field.text()[:-4]}Fire_Density.tif" + + with rasterio.open(tempClipResample, "w", **meta1) as dst: + dst.write(reclassified_ras, 1) + + processing.run("gdal:cliprasterbymasklayer", { + 'INPUT': tempClipResample, + 'MASK': countryLayer, + 'SOURCE_CRS': None, + 'TARGET_CRS': QgsCoordinateReferenceSystem(UTM_crs), + 'NODATA': 0, + 'ALPHA_BAND': False, + 'CROP_TO_CUTLINE': False, + 'KEEP_RESOLUTION': True, + 'SET_RESOLUTION': False, + 'X_RESOLUTION': None, + 'Y_RESOLUTION': None, + 'MULTITHREADING': False, + 'OPTIONS': '', + 'DATA_TYPE': 0, # Use 0 for the same data type as the input + 'EXTRA': '', + 'OUTPUT': rasOutput + }) + + self.dlg.ENV_Aggregate_Field.setText(rasOutput) + + # Copy style file + styleTemplate = os.path.join(current_script_path, "Style", f"{Dimension}.qml") + styleFileDestination = os.path.join(dimension_dir, "ENV") + if not os.path.exists(styleFileDestination): + os.mkdir(styleFileDestination) + styleFile = f"{os.path.splitext(rasOutput)[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ENV_status.setText("Processing Complete!") + self.dlg.ENV_status.repaint() + + if floodLayer: + processing.run( + "gdal:warpreproject", + { + "INPUT": floodLayer, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + with rasterio.open(tempResample, "r+") as src: + ENV_ras = src.read(1) + meta1 = src.meta + + #Rmax = ENV_ras.max() + #Rmin = ENV_ras.min() + #m_max = 5 + #m_min = 0 + #nodata_value = src.nodata + + #result = ((ENV_ras - Rmin) / (Rmax - Rmin)) * m_max + reclassified_ras = np.vectorize(self.reclassifyFloodHazard)(ENV_ras) + meta1.update(dtype=rasterio.float32) + + if os.path.exists(f"{workingDir}/{Dimension}/ENV"): + os.chdir(f"{workingDir}/{Dimension}/ENV") + else: + os.mkdir(f"{workingDir}/{Dimension}/ENV") + os.chdir(f"{workingDir}/{Dimension}/ENV") + + rasOutput = f"{self.dlg.ENV_Output_Field.text()[:-4]}Flood.tif" + + with rasterio.open(tempClipResample, "w", **meta1) as dst: + dst.write(reclassified_ras, 1) + + processing.run("gdal:cliprasterbymasklayer", { + 'INPUT': tempClipResample, + 'MASK': countryLayer, + 'SOURCE_CRS': None, + 'TARGET_CRS': QgsCoordinateReferenceSystem(UTM_crs), + 'NODATA': 0, + 'ALPHA_BAND': False, + 'CROP_TO_CUTLINE': False, + 'KEEP_RESOLUTION': True, + 'SET_RESOLUTION': False, + 'X_RESOLUTION': None, + 'Y_RESOLUTION': None, + 'MULTITHREADING': False, + 'OPTIONS': '', + 'DATA_TYPE': 0, # Use 0 for the same data type as the input + 'EXTRA': '', + 'OUTPUT': rasOutput + }) + + self.dlg.ENV_Aggregate_Field.setText(rasOutput) + + # Copy style file + styleTemplate = os.path.join(current_script_path, "Style", f"{Dimension}.qml") + styleFileDestination = os.path.join(dimension_dir, "ENV") + if not os.path.exists(styleFileDestination): + os.mkdir(styleFileDestination) + styleFile = f"{os.path.splitext(rasOutput)[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ENV_status.setText("Processing Complete!") + self.dlg.ENV_status.repaint() + + if landSlideLayer: + processing.run( + "gdal:warpreproject", + { + "INPUT": landSlideLayer, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + with rasterio.open(tempResample, "r+") as src: + ENV_ras = src.read(1) + meta1 = src.meta + + #Rmax = ENV_ras.max() + #Rmin = ENV_ras.min() + #m_max = 5 + #m_min = 0 + + #result = ((ENV_ras - Rmin) / (Rmax - Rmin)) * m_max + reclassified_ras = np.vectorize(self.reclassifyLandslide)(ENV_ras) + meta1.update(dtype=rasterio.float32) + + if os.path.exists(f"{workingDir}/{Dimension}/ENV"): + os.chdir(f"{workingDir}/{Dimension}/ENV") + else: + os.mkdir(f"{workingDir}/{Dimension}/ENV") + os.chdir(f"{workingDir}/{Dimension}/ENV") + + rasOutput = f"{self.dlg.ENV_Output_Field.text()[:-4]}Landslide_Susceptibility.tif" + + with rasterio.open(tempClipResample, "w", **meta1) as dst: + dst.write(reclassified_ras, 1) + + processing.run("gdal:cliprasterbymasklayer", { + 'INPUT': tempClipResample, + 'MASK': countryLayer, + 'SOURCE_CRS': None, + 'TARGET_CRS': QgsCoordinateReferenceSystem(UTM_crs), + 'NODATA': 0, + 'ALPHA_BAND': False, + 'CROP_TO_CUTLINE': False, + 'KEEP_RESOLUTION': True, + 'SET_RESOLUTION': False, + 'X_RESOLUTION': None, + 'Y_RESOLUTION': None, + 'MULTITHREADING': False, + 'OPTIONS': '', + 'DATA_TYPE': 0, # Use 0 for the same data type as the input + 'EXTRA': '', + 'OUTPUT': rasOutput + }) + + self.dlg.ENV_Aggregate_Field.setText(rasOutput) + + # Copy style file + styleTemplate = os.path.join(current_script_path, "Style", f"{Dimension}.qml") + styleFileDestination = os.path.join(dimension_dir, "ENV") + if not os.path.exists(styleFileDestination): + os.mkdir(styleFileDestination) + styleFile = f"{os.path.splitext(rasOutput)[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ENV_status.setText("Processing Complete!") + self.dlg.ENV_status.repaint() + + if cycloneLayer: + processing.run( + "gdal:warpreproject", + { + "INPUT": cycloneLayer, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + with rasterio.open(tempResample, "r+") as src: + ENV_ras = src.read(1) + meta1 = src.meta + + #Rmax = ENV_ras.max() + #Rmin = ENV_ras.min() + #m_max = 5 + #m_min = 0 + + #result = ((ENV_ras - Rmin) / (Rmax - Rmin)) * m_max + reclassified_ras = np.vectorize(self.reclassifyTropicalCyclone)(ENV_ras) + meta1.update(dtype=rasterio.float32) + + if os.path.exists(f"{workingDir}/{Dimension}/ENV"): + os.chdir(f"{workingDir}/{Dimension}/ENV") + else: + os.mkdir(f"{workingDir}/{Dimension}/ENV") + os.chdir(f"{workingDir}/{Dimension}/ENV") + + rasOutput = f"{self.dlg.ENV_Output_Field.text()[:-4]}Cyclones.tif" + + with rasterio.open(tempClipResample, "w", **meta1) as dst: + dst.write(reclassified_ras, 1) + + processing.run("gdal:cliprasterbymasklayer", { + 'INPUT': tempClipResample, + 'MASK': countryLayer, + 'SOURCE_CRS': None, + 'TARGET_CRS': QgsCoordinateReferenceSystem(UTM_crs), + 'NODATA': 0, + 'ALPHA_BAND': False, + 'CROP_TO_CUTLINE': False, + 'KEEP_RESOLUTION': True, + 'SET_RESOLUTION': False, + 'X_RESOLUTION': None, + 'Y_RESOLUTION': None, + 'MULTITHREADING': False, + 'OPTIONS': '', + 'DATA_TYPE': 0, # Use 0 for the same data type as the input + 'EXTRA': '', + 'OUTPUT': rasOutput + }) + + self.dlg.ENV_Aggregate_Field.setText(rasOutput) + + # Copy style file + styleTemplate = os.path.join(current_script_path, "Style", f"{Dimension}.qml") + styleFileDestination = os.path.join(dimension_dir, "ENV") + if not os.path.exists(styleFileDestination): + os.mkdir(styleFileDestination) + styleFile = f"{os.path.splitext(rasOutput)[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ENV_status.setText("Processing Complete!") + self.dlg.ENV_status.repaint() + + if droughtLayer: + processing.run( + "gdal:warpreproject", + { + "INPUT": droughtLayer, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": tempResample, + }, + ) + + with rasterio.open(tempResample, "r+") as src: + ENV_ras = src.read(1) + meta1 = src.meta + + #Rmax = ENV_ras.max() + #Rmin = ENV_ras.min() + #m_max = 5 + #m_min = 0 + # Retrieve the NoData value from the metadata + nodata_value = src.nodata + + #result = ((ENV_ras - Rmin) / (Rmax - Rmin)) * m_max + reclassified_ras = np.vectorize(self.reclassifyDrought)(ENV_ras, nodata_value) + meta1.update(dtype=rasterio.float32) + + if os.path.exists(f"{workingDir}/{Dimension}/ENV"): + os.chdir(f"{workingDir}/{Dimension}/ENV") + else: + os.mkdir(f"{workingDir}/{Dimension}/ENV") + os.chdir(f"{workingDir}/{Dimension}/ENV") + + rasOutput = f"{self.dlg.ENV_Output_Field.text()[:-4]}Drought.tif" + + with rasterio.open(tempClipResample, "w", **meta1) as dst: + dst.write(reclassified_ras, 1) + + processing.run("gdal:cliprasterbymasklayer", { + 'INPUT': tempClipResample, + 'MASK': countryLayer, + 'SOURCE_CRS': None, + 'TARGET_CRS': QgsCoordinateReferenceSystem(UTM_crs), + 'NODATA': 0, + 'ALPHA_BAND': False, + 'CROP_TO_CUTLINE': False, + 'KEEP_RESOLUTION': True, + 'SET_RESOLUTION': False, + 'X_RESOLUTION': None, + 'Y_RESOLUTION': None, + 'MULTITHREADING': False, + 'OPTIONS': '', + 'DATA_TYPE': 0, # Use 0 for the same data type as the input + 'EXTRA': '', + 'OUTPUT': rasOutput + }) + + self.dlg.ENV_Aggregate_Field.setText(rasOutput) + + # Copy style file + styleTemplate = os.path.join(current_script_path, "Style", f"{Dimension}.qml") + styleFileDestination = os.path.join(dimension_dir, "ENV") + if not os.path.exists(styleFileDestination): + os.mkdir(styleFileDestination) + styleFile = f"{os.path.splitext(rasOutput)[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ENV_status.setText("Processing Complete!") + self.dlg.ENV_status.repaint() + + except Exception as e: + print(str(e)) + self.dlg.ENV_status.setText(f"Error: {str(e)}") + self.dlg.ENV_status.repaint() + + # Define the reclassification function + def reclassifyFireHazards(self, value): + if value == 0: + return 5 + elif 0 < value <= 1: + return 4 + elif 1 < value <= 2: + return 3 + elif 2 < value <= 5: + return 2 + elif 5 < value <= 8: + return 1 + elif value > 8: + return 0 + else: + return np.nan # Handle NoData or unexpected values + + def reclassifyEarthQuakes(self, value): + if 0.01 <= value < 0.02: + return 5 + elif 0.02 < value <= 0.05: + return 4 + elif 0.05 < value <= 0.013: + return 3 + elif 0.013 < value <= 0.35: + return 2 + elif 0.35 < value <= 0.90: + return 1 + elif 0.90 < value <= 1.90: + return 0 + else: + return np.nan + + def reclassifyTsunami(self, value): + if value == 0: + return 5 + elif 0 < value <= 0.000001: + return 4 + elif 0.000001 < value <= 0.00001: + return 3 + elif 0.00001 < value <= 0.0001: + return 2 + elif 0.0001 < value <= 0.001: + return 1 + elif 0.001 < value <= 0.002: + return 0 + else: + return np.nan + + def reclassifyFloodHazard(self, value): + if value == 0: + return 5 + elif 0 < value <= 180: + return 4 + elif 180 < value <= 360: + return 3 + elif 360 < value <= 540: + return 2 + elif 540 < value <= 720: + return 1 + elif 720 < value <= 900: + return 0 + elif np.isnan(value) or value == nodata: + return 5 + + def reclassifyLandslide(self, value): + if value == 0: + return 5 + elif value == 1: + return 4 + elif value == 2: + return 3 + elif value == 3: + return 2 + elif value == 4: + return 1 + elif value > 5: + return 0 + else: + return 5 + + def reclassifyTropicalCyclone(self, value): + if value == 0: + return 5 + elif 0 < value <= 25: + return 4 + elif 25 < value <= 50: + return 3 + elif 50 < value <= 75: + return 2 + elif 75 < value <= 100: + return 1 + elif value > 100: + return 0 + else: + return np.nan + + def reclassifyDrought(self, value, nodata): + if value == 0: + return 5 + elif 0 < value <= 1: + return 4 + elif 1 < value <= 2: + return 3 + elif 2 < value <= 3: + return 2 + elif 3 < value <= 4: + return 1 + elif 4 < value <= 5: + return 0 + elif np.isnan(value) or value == nodata: + return 5 + + def reclassifyAirPollution(self, value): + if 0 < value <= 10: + return 5 + elif 10 < value <= 20: + return 4 + elif 20 < value <= 30: + return 3 + elif 30 < value <= 50: + return 2 + elif 50 < value <= 80: + return 1 + elif 80 < value <= 300: + return 0 + else: + return np.nan + + def envAggregate(self): + """ + This function is used in combination with the "natEnvironment" function. Due to there being numerous Natural Environment and Climatic factors that could + influence place characterization the rasterization and standardization has to be conducted for each hazard. This function aggregates each of the hazards + relating to Natural Environment and Climatic factors into a single standardized raster file. + + Factors it is applied: + Accessibility Dimension + - Natural Environment and Climatic factors + """ + # OUTPUT + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + Dimension = "Place Characterization" + ENV_Folder = f"{Dimension}/ENV" + + if os.path.exists(ENV_Folder): + os.chdir(ENV_Folder) + else: + pass + + rasOutput = self.dlg.ENV_AGGOutput_Field.text() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + self.dlg.ENVAGG_status.setText("Variables Set") + self.dlg.ENVAGG_status.repaint() + time.sleep(0.5) + self.dlg.ENVAGG_status.setText("Processing...") + self.dlg.ENVAGG_status.repaint() + + tif_list = [f for f in os.listdir(os.getcwd()) if f.endswith(".tif")] + raster_list = [] + + for ras in tif_list: + with rasterio.open(ras) as src: + raster_list.append(src.read(1)) + meta1 = src.meta + + len_raster_list = len(raster_list) + cumulative_sum = 0 + + for i in range(len_raster_list): + value = raster_list[i] + cumulative_sum += value + + aggregation = cumulative_sum / len_raster_list + os.chdir("..") + + with rasterio.open(rasOutput, "w", **meta1) as dst: + dst.write(aggregation, 1) + + self.dlg.ENV_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ENVAGG_status.setText("Processing Complete!") + self.dlg.ENVAGG_status.repaint() + + # *************************** Factor Aggregation Functions ************************************ # + + def contextualAggregation(self): + """ + This function performs a raster calculation aggregating all the contextual dimension factors according to their weightings + """ + self.dlg.contextualAggregation_Check.setText("") + self.dlg.contextualAggregation_Check.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + + # INPUT + WD_ras = self.dlg.WD_Aggregate_Field.text().strip(" ") + RF_ras = self.dlg.RF_Aggregate_Field.text().strip(" ") + FIN_ras = self.dlg.FIN_Aggregate_Field.text().strip(" ") + + WD_weight = self.dlg.WD_Aggregate_SB.value() + RF_weight = self.dlg.RF_Aggregate_SB.value() + FIN_weight = self.dlg.FIN_Aggregate_SB.value() + + # OUTPUT + aggregation = self.dlg.Contextual_AggregateOutput_Field.text() + + rasLayers = [WD_ras, RF_ras, FIN_ras] + factorWeighting = [WD_weight, RF_weight, FIN_weight] + non_empty_count = sum(1 for item in rasLayers if item != "") + + weightingSum = round(sum(factorWeighting)) + + if weightingSum == 100: + if "" in rasLayers: + missingLayers = [ + index for index, item in enumerate(rasLayers) if item == "" + ] + presentLayers = [ + index for index, item in enumerate(rasLayers) if item != "" + ] + + for i in missingLayers: + rasLayers[i] = rasLayers[presentLayers[0]] + factorWeighting[i] = 0 + + else: + pass + + weightingSum = round(sum(factorWeighting)) + if weightingSum == 100: + + with rasterio.open(rasLayers[0]) as src: + WD_ras = src.read(1) + WD_weight = factorWeighting[0] + meta1 = src.meta + + with rasterio.open(rasLayers[1]) as src: + RF_ras = src.read(1) + RF_weight = factorWeighting[1] + meta1 = src.meta + + with rasterio.open(rasLayers[2]) as src: + FIN_ras = src.read(1) + FIN_weight = factorWeighting[2] + + # Raster Calculation + + result = (WD_ras * WD_weight / 100) + (RF_ras * RF_weight / 100) + (FIN_ras * FIN_weight / 100) + + meta1.update(dtype=rasterio.float32) + + Dimension = "Contextual" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + with rasterio.open(aggregation, "w", **meta1) as dst: + dst.write(result, 1) + + self.dlg.CD_Aggregate_Field.setText( + f"{workingDir}{Dimension}/{aggregation}" + ) + + loggerCD = logging.getLogger("loggerCD") + loggerCD.setLevel(logging.INFO) + handlerCD = logging.FileHandler("Contextual.log") + formatterCD = logging.Formatter("%(asctime)s - %(message)s") + handlerCD.setFormatter(formatterCD) + loggerCD.addHandler(handlerCD) + + loggerCD.info( + f"Factors: {non_empty_count}/2 - {non_empty_count / 2 * 100} % - {non_empty_count}" + ) + logging.shutdown() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{aggregation.split('.')[0]}.qml" + + shutil.copy( + styleTemplate, os.path.join(styleFileDestination, styleFile) + ) + + time.sleep(1) + + layer = QgsRasterLayer(aggregation, f"{aggregation}") + + if not layer.isValid(): + print("Layer failed to load!") + + QgsProject.instance().addMapLayer(layer) + + self.dlg.contextualAggregation_Check.setText( + "Contextual dimension aggregation complete!" + ) + else: + self.dlg.contextualAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + + else: + self.dlg.contextualAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + + os.chdir(workingDir) + + def accessibiltyAggregation(self): + """ + This function performs a raster calculation aggregating all the accessibilty dimension factors according to their weightings + """ + self.dlg.accessibilityAggregation_Check.setText("") + self.dlg.accessibilityAggregation_Check.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + + # INPUT + WTP_ras = self.dlg.WTP_Aggregate_Field.text().strip(" ") + PBT_ras = self.dlg.PBT_Aggregate_Field.text().strip(" ") + ETF_ras = self.dlg.ETF_Aggregate_Field.text().strip(" ") + HEA_ras = self.dlg.HEA_Aggregate_Field.text().strip(" ") + FIF_ras = self.dlg.FIF_Aggregate_Field.text().strip(" ") + + WTP_weight = self.dlg.WTP_Aggregate_SB.value() + PBT_weight = self.dlg.PBT_Aggregate_SB.value() + ETF_weight = self.dlg.ETF_Aggregate_SB.value() + HEA_weight = self.dlg.HEA_Aggregate_SB.value() + FIF_weight = self.dlg.FIF_Aggregate_SB.value() + + # OUTPUT + aggregation = self.dlg.Accessibility_AggregateOutput_Field.text() + + rasLayers = [WTP_ras, PBT_ras, ETF_ras, HEA_ras, FIF_ras] + factorWeighting = [ + WTP_weight, + PBT_weight, + ETF_weight, + HEA_weight, + FIF_weight, + ] + non_empty_count = sum(1 for item in rasLayers if item != "") + + weightingSum = round(sum(factorWeighting)) + + if weightingSum == 100: + if "" in rasLayers: + missingLayers = [ + index for index, item in enumerate(rasLayers) if item == "" + ] + presentLayers = [ + index for index, item in enumerate(rasLayers) if item != "" + ] + + for i in missingLayers: + rasLayers[i] = rasLayers[presentLayers[0]] + factorWeighting[i] = 0 + + else: + pass + + weightingSum = round(sum(factorWeighting)) + if weightingSum == 100: + with rasterio.open(rasLayers[0]) as src: + WTP_ras = src.read(1) + WTP_weight = factorWeighting[0] + meta1 = src.meta + + with rasterio.open(rasLayers[1]) as src: + PBT_ras = src.read(1) + PBT_weight = factorWeighting[1] + + with rasterio.open(rasLayers[2]) as src: + ETF_ras = src.read(1) + ETF_weight = factorWeighting[2] + + with rasterio.open(rasLayers[3]) as src: + HEA_ras = src.read(1) + HEA_weight = factorWeighting[3] + + with rasterio.open(rasLayers[4]) as src: + FIF_ras = src.read(1) + FIF_weight = factorWeighting[4] + + # Raster Calculation + + result = ( + (WTP_ras * WTP_weight / 100) + + (PBT_ras * PBT_weight / 100) + + (ETF_ras * ETF_weight / 100) + + (HEA_ras * HEA_weight / 100) + + (FIF_ras * FIF_weight / 100) + ) + + meta1.update(dtype=rasterio.float32) + + Dimension = "Accessibility" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + with rasterio.open(aggregation, "w", **meta1) as dst: + dst.write(result, 1) + + self.dlg.AD_Aggregate_Field.setText( + f"{workingDir}{Dimension}/{aggregation}" + ) + + loggerAD = logging.getLogger("loggerAD") + loggerAD.setLevel(logging.INFO) + handlerAD = logging.FileHandler("Accessibility.log") + formatterAD = logging.Formatter("%(asctime)s - %(message)s") + handlerAD.setFormatter(formatterAD) + loggerAD.addHandler(handlerAD) + + loggerAD.info( + f"Factors: {non_empty_count}/6 - {non_empty_count / 6 * 100} % - {non_empty_count}" + ) + logging.shutdown() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{aggregation.split('.')[0]}.qml" + + shutil.copy( + styleTemplate, os.path.join(styleFileDestination, styleFile) + ) + + layer = QgsRasterLayer(aggregation, f"{aggregation}") + + if not layer.isValid(): + print("Layer failed to load!") + + QgsProject.instance().addMapLayer(layer) + + self.dlg.accessibilityAggregation_Check.setText( + "Accessibility dimension aggregation complete!" + ) + else: + self.dlg.accessibilityAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + else: + self.dlg.accessibilityAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + + os.chdir(workingDir) + + def placeCharacterizationAggregation(self): + """ + This function performs a raster calculation aggregating all the place characterization dimension factors according to their weightings + """ + self.dlg.placeCharacterizationAggregation_Check.setText("") + self.dlg.placeCharacterizationAggregation_Check.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + + # INPUT + WLK_ras = self.dlg.AT_Aggregate_Field.text().strip(" ") + # CYC_ras = self.dlg.CYC_Aggregate_Field.text().strip(" ") + SAF_ras = self.dlg.SAF_Aggregate_Field.text().strip(" ") + DIG_ras = self.dlg.DIG_Aggregate_Field.text().strip(" ") + ENV_ras = self.dlg.ENV_Aggregate_Field.text().strip(" ") + EDU_ras = self.dlg.EDU_Aggregate_Field.text().strip(" ") + FCV_ras = self.dlg.FCV_Aggregate_Field.text().strip(" ") + WAS_ras = self.dlg.WAS_Aggregate_Field.text().strip(" ") + + WLK_weight = self.dlg.WLK_Aggregate_SB.value() + # CYC_weight = self.dlg.CYC_Aggregate_SB.value() + SAF_weight = self.dlg.SAF_Aggregate_SB.value() + DIG_weight = self.dlg.DIG_Aggregate_SB.value() + ENV_weight = self.dlg.ENV_Aggregate_SB.value() + EDU_weight = self.dlg.EDU_Aggregate_SB.value() + FCV_weight = self.dlg.FCV_Aggregate_SB.value() + WAS_weight = self.dlg.WAS_Aggregate_SB.value() + + # OUTPUT + aggregation = self.dlg.PlaceCharacterization_AggregateOutput_Field.text() + + rasLayers = [ + WLK_ras, + SAF_ras, + DIG_ras, + ENV_ras, + EDU_ras, + FCV_ras, + WAS_ras, + ] + factorWeighting = [ + WLK_weight, + SAF_weight, + DIG_weight, + ENV_weight, + EDU_weight, + FCV_weight, + WAS_weight, + ] + non_empty_count = sum(1 for item in rasLayers if item != "") + + weightingSum = round(sum(factorWeighting)) + + if weightingSum == 100: + if "" in rasLayers: + missingLayers = [ + index for index, item in enumerate(rasLayers) if item == "" + ] + presentLayers = [ + index for index, item in enumerate(rasLayers) if item != "" + ] + + for i in missingLayers: + rasLayers[i] = rasLayers[presentLayers[0]] + factorWeighting[i] = 0 + + else: + pass + + weightingSum = round(sum(factorWeighting)) + if weightingSum == 100: + + with rasterio.open(rasLayers[0]) as src: + WLK_ras = src.read(1) + WLK_weight = factorWeighting[0] + meta1 = src.meta + + with rasterio.open(rasLayers[1]) as src: + SAF_ras = src.read(1) + SAF_weight = factorWeighting[1] + + with rasterio.open(rasLayers[2]) as src: + DIG_ras = src.read(1) + DIG_weight = factorWeighting[2] + + with rasterio.open(rasLayers[3]) as src: + ENV_ras = src.read(1) + ENV_weight = factorWeighting[3] + + with rasterio.open(rasLayers[4]) as src: + EDU_ras = src.read(1) + EDU_weight = factorWeighting[4] + + with rasterio.open(rasLayers[5]) as src: + FCV_ras = src.read(1) + FCV_weight = factorWeighting[5] + + with rasterio.open(rasLayers[6]) as src: + WAS_ras = src.read(1) + WAS_weight = factorWeighting[6] + + # Raster Calculation + + result = ( + (WLK_ras * WLK_weight / 100) + + (SAF_ras * SAF_weight / 100) + + (DIG_ras * DIG_weight / 100) + + (ENV_ras * ENV_weight / 100) + + (EDU_ras * EDU_weight / 100) + + (FCV_ras * FCV_weight / 100) + + (WAS_ras * WAS_weight / 100) + ) + + meta1.update(dtype=rasterio.float32) + + Dimension = "Place Characterization" + if os.path.exists(Dimension): + os.chdir(Dimension) + else: + os.mkdir(Dimension) + os.chdir(Dimension) + + with rasterio.open(aggregation, "w", **meta1) as dst: + dst.write(result, 1) + + self.dlg.PD_Aggregate_Field.setText( + f"{workingDir}{Dimension}/{aggregation}" + ) + + loggerPD = logging.getLogger("loggerPD") + loggerPD.setLevel(logging.INFO) + handlerPD = logging.FileHandler("Place Characterization.log") + formatterPD = logging.Formatter("%(asctime)s - %(message)s") + handlerPD.setFormatter(formatterPD) + loggerPD.addHandler(handlerPD) + + loggerPD.info( + f"Factors: {non_empty_count}/10 - {non_empty_count / 10 * 100} % - {non_empty_count}" + ) + logging.shutdown() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{aggregation.split('.')[0]}.qml" + + shutil.copy( + styleTemplate, os.path.join(styleFileDestination, styleFile) + ) + + layer = QgsRasterLayer(aggregation, f"{aggregation}") + + if not layer.isValid(): + print("Layer failed to load!") + + QgsProject.instance().addMapLayer(layer) + + self.dlg.placeCharacterizationAggregation_Check.setText( + "Place Characterization dimension aggregation complete!" + ) + else: + self.dlg.placeCharacterizationAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + else: + self.dlg.placeCharacterizationAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + + os.chdir(workingDir) + + def dimesnionsAggregation(self): + """ + This function performs a final raster calculation aggregating all the dimension aggregation output raster according to their weightings + """ + self.dlg.dimensionAggregation_Check.setText("") + self.dlg.dimensionAggregation_Check.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + + # INPUT + #ID_ras = self.dlg.ID_Aggregate_Field.text().strip(" ") + CD_ras = self.dlg.CD_Aggregate_Field.text().strip(" ") + AD_ras = self.dlg.AD_Aggregate_Field.text().strip(" ") + PD_ras = self.dlg.PD_Aggregate_Field.text().strip(" ") + + #ID_weight = self.dlg.ID_Aggregate_SB.value() + CD_weight = self.dlg.CD_Aggregate_SB.value() + AD_weight = self.dlg.AD_Aggregate_SB.value() + PD_weight = self.dlg.PD_Aggregate_SB.value() + + # OUTPUT + aggregation = self.dlg.Dimensions_AggregateOutput_Field.text() + + rasLayers = [CD_ras, AD_ras, PD_ras] + dimensionWeighting = [CD_weight, AD_weight, PD_weight] + + weightingSum = round(sum(dimensionWeighting)) + + if weightingSum == 100: + if "" in rasLayers: + missingLayers = [ + index for index, item in enumerate(rasLayers) if item == "" + ] + presentLayers = [ + index for index, item in enumerate(rasLayers) if item != "" + ] + + for i in missingLayers: + rasLayers[i] = rasLayers[presentLayers[0]] + dimensionWeighting[i] = 0 + + else: + pass + + weightingSum = round(sum(dimensionWeighting)) + if weightingSum == 100: + + with rasterio.open(rasLayers[0]) as src: + CD_ras = src.read(1) + CD_weight = dimensionWeighting[0] + meta1 = src.meta + + with rasterio.open(rasLayers[1]) as src: + AD_ras = src.read(1) + AD_weight = dimensionWeighting[1] + + with rasterio.open(rasLayers[2]) as src: + PD_ras = src.read(1) + PD_weight = dimensionWeighting[2] + + # Raster Calculation + + result = ( + (CD_ras * CD_weight / 100) + + (AD_ras * AD_weight / 100) + + (PD_ras * PD_weight / 100) + ) + result[result == 0] = src.nodata + + meta1.update(dtype=rasterio.float32) + + Final_output = "Final_output" + if os.path.exists(Final_output): + os.chdir(Final_output) + else: + os.mkdir(Final_output) + os.chdir(Final_output) + + with rasterio.open(aggregation, "w", **meta1) as dst: + dst.write(result, 1) + + styleTemplate = f"{current_script_path}/Style/Final.qml" + styleFileDestination = f"{workingDir}{Final_output}/" + styleFile = f"{aggregation.split('.')[0]}.qml" + + shutil.copy( + styleTemplate, os.path.join(styleFileDestination, styleFile) + ) + + log_list = [ + "Contextual", + "Accessibility", + "Place Characterization", + ] + factor_num = [] + + for dimension in log_list: + log_file = f"{workingDir}{dimension}/{dimension}.log" + if os.path.exists(log_file): + with open(log_file, "r") as file: + lines = file.readlines() + + if lines: + last_line = lines[-1].split("-")[-1].strip() + else: + pass + + factor_num.append(last_line) + else: + pass + + integer_list = [int(item) for item in factor_num] + sum_list = sum(integer_list) + Confidence = round(sum_list / 15 * 100, 2) + + layer = QgsRasterLayer(aggregation, f"{aggregation}") + + if not layer.isValid(): + print("Layer failed to load!") + + QgsProject.instance().addMapLayer(layer) + + self.dlg.FinalAggregation_Check.setText( + f"Dimensional aggregation complete! - Confidence: {sum_list}/15 factors used. ({Confidence} %)" + ) + else: + self.dlg.dimensionAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + + else: + self.dlg.dimensionAggregation_Check.setText( + "Weighting % does not add up to 100 %" + ) + + os.chdir(workingDir) + + # *************************** Insights Tab Functions *********************************** # + def scoreReclassInsights(self): + """ + This function takes the final aggregate score, a dimension aggregate score, or even a single factor output raster and classifies it into five discrete classes of enablement. + """ + self.dlg.Enablement_status.setText("") + self.dlg.Enablement_status.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + + os.chdir(workingDir) + Insights_folder = "Insights" + if os.path.exists(Insights_folder): + pass + else: + os.mkdir(Insights_folder) + + tempDir = f"temp" + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + # INPUT + score = self.dlg.Insights_Score_Input_Field.filePath() + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + + # Temp OUTPUT + countryUTMLayer = f"{tempDir}/countryUTMLayer.shp" + countryUTMLayerBuf = f"{tempDir}/countryUTMLayerBuf.shp" + ScoretempResample = f"{tempDir}/ScoretempResample.tif" + + self.dlg.Enablement_status.setText("Variables Set") + self.dlg.Enablement_status.repaint() + + self.dlg.Enablement_status.setText("Processing...") + self.dlg.Enablement_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + shp_utm.to_file(countryUTMLayer) + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": countryUTMLayerBuf, + }, + ) + + CountryBuf_df = gpd.read_file(countryUTMLayerBuf) + country_extent = CountryBuf_df.total_bounds + + processing.run( + "gdal:warpreproject", + { + "INPUT": score, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": ScoretempResample, + }, + ) + + Insights_enablement = "1) Level of Enablement Classification" + if os.path.exists(f"{Insights_folder}/{Insights_enablement}"): + pass + else: + os.mkdir(f"{Insights_folder}/{Insights_enablement}") + + # Score Reclassify + with rasterio.open(ScoretempResample) as src: + score_ras = src.read(1) + meta1 = src.meta + + # Raster Calculation + + result = ( + 0 * (score_ras <= 0.5) + + 1 * (score_ras > 0.5) * (score_ras <= 1.5) + + 2 * (score_ras > 1.5) * (score_ras <= 2.5) + + 3 * (score_ras > 2.5) * (score_ras <= 3.5) + + 4 * (score_ras > 3.5) * (score_ras <= 4.5) + + 5 * (score_ras > 4.5) + ) + + meta1.update(dtype=rasterio.float32) + + score_rec = f"{workingDir}{Insights_folder}/{Insights_enablement}/Level_of_Enablement.tif" + with rasterio.open(score_rec, "w", **meta1) as dst: + dst.write(result, 1) + + styleTemplate = f"{current_script_path}/Style/Insights Score.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_enablement}" + styleFile = f"{score_rec.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.Insights_ScoreReclass_Input_Field.setFilePath(f"{score_rec}") + + self.dlg.Enablement_status.setText("Classification Complete!") + self.dlg.Enablement_status.repaint() + + def populationReclassInsights(self): + """ + This function takes a population count raster as input and classifies it into 3 discrete classes based on + the lower quartile range, interquartile range, and upper quartile range of data to identify + areas of relatively low, medium, and high population per region. + """ + self.dlg.Population_status.setText("") + self.dlg.Population_status.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + + os.chdir(workingDir) + Insights_folder = "Insights" + if os.path.exists(Insights_folder): + pass + else: + os.mkdir(Insights_folder) + + tempDir = f"temp" + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + # INPUT + population = self.dlg.Insights_Population_Input_Field.filePath() + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + + # Temp OUTPUT + countryUTMLayer = f"{tempDir}/countryUTMLayer.shp" + countryUTMLayerBuf = f"{tempDir}/countryUTMLayerBuf.shp" + PoptempResample = f"{tempDir}/PoptempResample.tif" + + self.dlg.Population_status.setText("Variables Set") + self.dlg.Population_status.repaint() + + self.dlg.Population_status.setText("Processing...") + self.dlg.Population_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + shp_utm.to_file(countryUTMLayer) + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": countryUTMLayerBuf, + }, + ) + + CountryBuf_df = gpd.read_file(countryUTMLayerBuf) + country_extent = CountryBuf_df.total_bounds + + processing.run( + "gdal:warpreproject", + { + "INPUT": population, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": PoptempResample, + }, + ) + + Insights_population = "2) Relative Population Count Classification" + if os.path.exists(f"{Insights_folder}/{Insights_population}"): + pass + else: + os.mkdir(f"{Insights_folder}/{Insights_population}") + + # Population Reclassify + with rasterio.open(PoptempResample) as src: + pop_ras = src.read(1) + meta1 = src.meta + + pop_ras[pop_ras == src.nodata] = -1 + masked_raster = np.ma.masked_where(pop_ras == -1, pop_ras) + + # out_image, out_transform = np.ma.mask(masked_raster, countryUTMLayer, invert=True) + + percentile_25 = np.percentile(masked_raster.compressed(), 25) + percentile_75 = np.percentile(masked_raster.compressed(), 75) + + # # Raster Calculation + # + result = ( + 0 * (pop_ras == -1) + + 1 * (pop_ras > -1) * (pop_ras <= percentile_25) + + 2 * (pop_ras > percentile_25) * (pop_ras <= percentile_75) + + 3 * (pop_ras > percentile_75) + ) + + # result = pop_ras + + meta1.update(dtype=rasterio.float32) + + pop_rec = f"{workingDir}{Insights_folder}/{Insights_population}/Relative_Population_Count.tif" + with rasterio.open(pop_rec, "w", **meta1) as dst: + dst.write(result, 1) + + styleTemplate = f"{current_script_path}/Style/Insights Population.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_population}/" + styleFile = f"{pop_rec.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.Insights_PopulationReclass_Input_Field.setFilePath(f"{pop_rec}") + + self.dlg.Population_status.setText("Classification Complete!") + self.dlg.Population_status.repaint() + + def combineReclassInsights(self): + """ + Through the use of raster calculation this function combines the discrete level of enablement raster produced by the "scoreReclassInsights" + function and the relative population count raster produced by the "populationReclassInsights" function. A single raster layer is produced + contains 15 classes. + """ + self.dlg.Combine_status.setText("") + self.dlg.Combine_status.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + + os.chdir(workingDir) + Insights_folder = "Insights" + if os.path.exists(Insights_folder): + pass + else: + os.mkdir(Insights_folder) + + tempDir = f"temp" + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + # INPUT + score_rec = self.dlg.Insights_ScoreReclass_Input_Field.filePath() + pop_rec = self.dlg.Insights_PopulationReclass_Input_Field.filePath() + + Insights_combine = ( + "3) Combined Level of Enablement & Relative Population Count Classification" + ) + if os.path.exists(f"{Insights_folder}/{Insights_combine}"): + pass + else: + os.mkdir(f"{Insights_folder}/{Insights_combine}") + + self.dlg.Combine_status.setText("Variables Set") + self.dlg.Combine_status.repaint() + + self.dlg.Combine_status.setText("Processing...") + self.dlg.Combine_status.repaint() + + # Combine Population and Score Reclassify + with rasterio.open(score_rec) as src: + score_rec_ras = src.read(1) + meta1 = src.meta + + with rasterio.open(pop_rec) as src: + pop_rec_ras = src.read(1) + + # Raster Calculation + + result = ( + 1 * (score_rec_ras == 1) * (pop_rec_ras == 1) + + 2 * (score_rec_ras == 1) * (pop_rec_ras == 2) + + 3 * (score_rec_ras == 1) * (pop_rec_ras == 3) + + 4 * (score_rec_ras == 2) * (pop_rec_ras == 1) + + 5 * (score_rec_ras == 2) * (pop_rec_ras == 2) + + 6 * (score_rec_ras == 2) * (pop_rec_ras == 3) + + 7 * (score_rec_ras == 3) * (pop_rec_ras == 1) + + 8 * (score_rec_ras == 3) * (pop_rec_ras == 2) + + 9 * (score_rec_ras == 3) * (pop_rec_ras == 3) + + 10 * (score_rec_ras == 4) * (pop_rec_ras == 1) + + 11 * (score_rec_ras == 4) * (pop_rec_ras == 2) + + 12 * (score_rec_ras == 4) * (pop_rec_ras == 3) + + 13 * (score_rec_ras == 5) * (pop_rec_ras == 1) + + 14 * (score_rec_ras == 5) * (pop_rec_ras == 2) + + 15 * (score_rec_ras == 5) * (pop_rec_ras == 3) + ) + + meta1.update(dtype=rasterio.float32) + + combined_rec = f"{workingDir}{Insights_folder}/{Insights_combine}/Enablement_&_Population_Combined_classification.tif" + with rasterio.open(combined_rec, "w", **meta1) as dst: + dst.write(result, 1) + + styleTemplate = f"{current_script_path}/Style/Insights Combined.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_combine}/" + styleFile = f"{combined_rec.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.Insights_Reclass_Input_Field.setFilePath(f"{combined_rec}") + self.dlg.Insights_Agg_Input_Field.setFilePath(f"{combined_rec}") + self.dlg.Insights_Buf_Input_Field.setFilePath(f"{combined_rec}") + + layer0 = QgsRasterLayer( + combined_rec, f"Enablement_&_Population_Combined_classification" + ) + QgsProject.instance().addMapLayer(layer0) + + self.dlg.Combine_status.setText("Classification Complete!") + self.dlg.Combine_status.repaint() + + def Aggregationinsights(self): + """ + this function takes the combine raster output produced by the "combineReclassInsights" function and aggregates, by extracting + the to the majority class to a polygon layer representing boundaries of interest for aggregation (e.g. municipal boundary layer). + """ + self.dlg.Aggregate_status.setText("") + self.dlg.Aggregate_status.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + + os.chdir(workingDir) + Insights_folder = "Insights" + if os.path.exists(Insights_folder): + pass + else: + os.mkdir(Insights_folder) + + tempDir = f"temp" + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + # INPUT + insights_raster = self.dlg.Insights_Agg_Input_Field.filePath() + aggregation_polygon = self.dlg.Insights_Polygon_Input_Field.filePath() + + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + + # TEMP OUTPUT + aggregation_polygon_utm = f"{tempDir}/aggregation_polygon_utm.shp" + + self.dlg.Aggregate_status.setText("Variables Set") + self.dlg.Aggregate_status.repaint() + + self.dlg.Aggregate_status.setText("Processing...") + self.dlg.Aggregate_status.repaint() + + self.convertCRS(aggregation_polygon, UTM_crs) + shp_utm.to_file(aggregation_polygon_utm) + + Insights_aggregation = "4) Aggregation" + if os.path.exists(f"{Insights_folder}/{Insights_aggregation}"): + pass + else: + os.mkdir(f"{Insights_folder}/{Insights_aggregation}") + + shpOutput = ( + f"{workingDir}{Insights_folder}/{Insights_aggregation}/" + + self.dlg.AGG_Output_Field.text() + + ".shp" + ) + + processing.run( + "native:zonalstatisticsfb", + { + "INPUT": QgsProcessingFeatureSourceDefinition( + aggregation_polygon_utm, + selectedFeaturesOnly=False, + featureLimit=-1, + flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck, + geometryCheck=QgsFeatureRequest.GeometrySkipInvalid, + ), + "INPUT_RASTER": insights_raster, + "RASTER_BAND": 1, + "COLUMN_PREFIX": "_", + "STATISTICS": [9], + "OUTPUT": shpOutput, + }, + ) + + styleTemplate = f"{current_script_path}/Style/Insights Aggregation.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_aggregation}/" + styleFile = f"{self.dlg.AGG_Output_Field.text()}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.Insights_AGGReclass_Input_Field.setFilePath(f"{shpOutput}") + self.dlg.Insights_AGGReclass_Input_Field_2.setFilePath(f"{shpOutput}") + + self.dlg.Aggregate_status.setText("Aggregation Complete!") + self.dlg.Aggregate_status.repaint() + + def reZones(self): + self.dlg.REzone_status.setText("") + self.dlg.REzone_status.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + + os.chdir(workingDir) + Insights_folder = "Insights" + if os.path.exists(Insights_folder): + pass + else: + os.mkdir(Insights_folder) + + tempDir = f"temp" + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + # INPUT + reclassified_layer = self.dlg.Insights_Reclass_Input_Field.filePath() + re_zones = self.dlg.Insights_RE_Input_Field.filePath() + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + countryLayer = self.dlg.countryLayer_Field.filePath() + pixelSize = self.dlg.pixelSize_SB.value() + aggregate = self.dlg.Insights_AGGReclass_Input_Field.filePath() + + # Temp OUTPUT + REtempResample = f"{tempDir}/REtempResample.tif" + countryUTMLayerBuf = f"{tempDir}/countryUTMLayerBuf.shp" + poly_temp = f"{tempDir}/poly_temp.shp" + temp_dis = f"{tempDir}/temp_dis.shp" + final_temp_dis = f"{tempDir}/final_temp_dis.shp" + + self.dlg.REzone_status.setText("Variables Set") + self.dlg.REzone_status.repaint() + + self.dlg.REzone_status.setText("Processing...") + self.dlg.REzone_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + countryUTMLayer = QgsVectorLayer(shp_utm.to_json(), "countryUTMLayer", "ogr") + + buffer = processing.run( + "native:buffer", + { + "INPUT": countryUTMLayer, + "DISTANCE": self.BUFFER_DISTANCE, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": True, + "SEPARATE_DISJOINT": False, + "OUTPUT": countryUTMLayerBuf, + }, + ) + + CountryBuf_df = gpd.read_file(countryUTMLayerBuf) + country_extent = CountryBuf_df.total_bounds + + processing.run( + "gdal:warpreproject", + { + "INPUT": re_zones, + "SOURCE_CRS": None, + "TARGET_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "RESAMPLING": 0, + "NODATA": None, + "TARGET_RESOLUTION": pixelSize, + "OPTIONS": "", + "DATA_TYPE": 0, + "TARGET_EXTENT": f"{country_extent[0]},{country_extent[2]},{country_extent[1]},{country_extent[3]} [{UTM_crs}]", + "TARGET_EXTENT_CRS": QgsCoordinateReferenceSystem(UTM_crs), + "MULTITHREADING": False, + "EXTRA": "", + "OUTPUT": REtempResample, + }, + ) + + Insights_re_raster = "5) RE Zone Raster Locations" + if os.path.exists(f"{Insights_folder}/{Insights_re_raster}"): + pass + else: + os.mkdir(f"{Insights_folder}/{Insights_re_raster}") + + # Reclassified rasters join to RE zones + with rasterio.open(reclassified_layer) as src: + reclassified_layer_ras = src.read(1) + meta1 = src.meta + + with rasterio.open(REtempResample) as src: + re_zones = src.read(1) + re_zones[np.isinf(re_zones)] = src.nodata + re_zones[re_zones != src.nodata] = 1 + + # Raster Calculation + result = reclassified_layer_ras * re_zones + + meta1.update(dtype=rasterio.float32) + + combined_RE = ( + f"{workingDir}{Insights_folder}/{Insights_re_raster}/" + + self.dlg.RE_Output_Field.text() + + "Enablement_&_Population_Combined.tif" + ) + with rasterio.open(combined_RE, "w", **meta1) as dst: + dst.write(result, 1) + + styleTemplate = f"{current_script_path}/Style/Insights Combined.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_re_raster}/" + styleFile = f"{combined_RE.split('.')[0]}.qml" + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + # Vectorize raster + Polygon = processing.run( + "gdal:polygonize", + { + "INPUT": combined_RE, + "BAND": 1, + "FIELD": "DN", + "EIGHT_CONNECTEDNESS": False, + "EXTRA": "", + "OUTPUT": poly_temp, + }, + ) + + Dissolve = processing.run( + "native:dissolve", + { + "INPUT": poly_temp, + "FIELD": ["DN"], + "SEPARATE_DISJOINT": False, + "OUTPUT": temp_dis, + }, + ) + + gdf = gpd.read_file(temp_dis) + gdf = gdf[(gdf["DN"] > 0) & (gdf["DN"] < 15)] + gdf.to_file(final_temp_dis) + + # Load the main polygon layer + main_layer = gpd.read_file(aggregate) + + # Load the layer you want to check for intersection + intersect_layer = gpd.read_file(final_temp_dis) + + # Perform a spatial join + intersecting_records = gpd.sjoin( + main_layer, intersect_layer, how="inner", op="intersects" + ) + + # Extract the intersecting polygons + intersecting_polygons = main_layer.loc[intersecting_records.index] + + # Save to a new shapefile + admin_RE = ( + f"{workingDir}{Insights_folder}/{Insights_re_raster}/" + + self.dlg.RE_Output_Field.text() + + "admin_units_intersection.shp" + ) + intersecting_polygons.to_file(admin_RE) + + styleTemplate = f"{current_script_path}/Style/Insights Aggregation.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_re_raster}/" + styleFile = f"{admin_RE.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + layer4 = QgsVectorLayer( + admin_RE, f"{self.dlg.RE_Output_Field.text()}admin_unit_intersection" + ) + QgsProject.instance().addMapLayer(layer4) + + layer3 = QgsRasterLayer( + combined_RE, + f"{self.dlg.RE_Output_Field.text()}Enablement_&_Population_Combined", + ) + QgsProject.instance().addMapLayer(layer3) + + self.dlg.REzone_status.setText("RE Zones extraction complete!") + self.dlg.REzone_status.repaint() + + def Bufferinsights(self): + self.dlg.REpoint_status.setText("") + self.dlg.REpoint_status.repaint() + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + + os.chdir(workingDir) + Insights_folder = "Insights" + if os.path.exists(Insights_folder): + pass + else: + os.mkdir(Insights_folder) + + tempDir = f"temp" + if os.path.exists(tempDir): + shutil.rmtree(tempDir) + else: + pass + + time.sleep(0.5) + os.mkdir(tempDir) + + # INPUT + insights_raster = self.dlg.Insights_Buf_Input_Field.filePath() + location_point = self.dlg.Insights_Points_Input_Field.filePath() + countryLayer = self.dlg.countryLayer_Field.filePath() + aggregate = self.dlg.Insights_AGGReclass_Input_Field_2.filePath() + bufferDistance = self.dlg.bufferDistance_SB.value() + + UTM_crs = str(self.dlg.mQgsProjectionSelectionWidget.crs()).split(" ")[-1][:-1] + + # TEMP OUTPUT + adminUTMLayer = f"{tempDir}/adminUTMLayer.shp" + location_point_utm = f"{tempDir}/aggregation_polygon_utm.shp" + location_buffer = f"{tempDir}/location_buffer.shp" + location_buffer_clip = f"{tempDir}/location_buffer_clip.shp" + + self.dlg.REpoint_status.setText("Variables Set") + self.dlg.REpoint_status.repaint() + + self.dlg.REpoint_status.setText("Processing...") + self.dlg.REpoint_status.repaint() + + self.convertCRS(countryLayer, UTM_crs) + shp_utm.to_file(adminUTMLayer) + + self.convertCRS(location_point, UTM_crs) + location_point_utm = QgsVectorLayer( + shp_utm.to_json(), "location_point_utm", "ogr" + ) + + processing.run( + "native:buffer", + { + "INPUT": location_point_utm, + "DISTANCE": bufferDistance, + "SEGMENTS": 5, + "END_CAP_STYLE": 0, + "JOIN_STYLE": 0, + "MITER_LIMIT": 2, + "DISSOLVE": False, + "SEPARATE_DISJOINT": False, + "OUTPUT": location_buffer, + }, + ) + + processing.run( + "native:clip", + { + "INPUT": location_buffer, + "OVERLAY": QgsProcessingFeatureSourceDefinition( + adminUTMLayer, + selectedFeaturesOnly=False, + featureLimit=-1, + flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck, + geometryCheck=QgsFeatureRequest.GeometrySkipInvalid, + ), + "OUTPUT": location_buffer_clip, + }, + ) + + Insights_re_point = "6) RE Point Locations" + if os.path.exists(f"{Insights_folder}/{Insights_re_point}"): + pass + else: + os.mkdir(f"{Insights_folder}/{Insights_re_point}") + + shpOutput = ( + f"{workingDir}{Insights_folder}/{Insights_re_point}/" + + self.dlg.Buffer_Output_Field.text() + + f"{str(bufferDistance)}m_buffer.shp" + ) + + processing.run( + "native:zonalstatisticsfb", + { + "INPUT": QgsProcessingFeatureSourceDefinition( + location_buffer_clip, + selectedFeaturesOnly=False, + featureLimit=-1, + flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck, + geometryCheck=QgsFeatureRequest.GeometrySkipInvalid, + ), + "INPUT_RASTER": insights_raster, + "RASTER_BAND": 1, + "COLUMN_PREFIX": "_", + "STATISTICS": [9], + "OUTPUT": shpOutput, + }, + ) + + styleTemplate = f"{current_script_path}/Style/Insights Buffer Aggregation.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_re_point}/" + styleFile = ( + f"{self.dlg.Buffer_Output_Field.text()}{str(bufferDistance)}m_buffer.qml" + ) + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + # Load the main polygon layer + main_layer = gpd.read_file(aggregate) + + # Load the layer you want to check for intersection + intersect_layer = gpd.read_file(shpOutput) + + # Perform a spatial join + intersecting_records = gpd.sjoin( + main_layer, intersect_layer, how="inner", op="intersects" + ) + + # Extract the intersecting polygons + intersecting_polygons = main_layer.loc[intersecting_records.index] + + # Save to a new shapefile + admin_RE = ( + f"{workingDir}{Insights_folder}/{Insights_re_point}/" + + self.dlg.Buffer_Output_Field.text() + + f"admin_unit_{str(bufferDistance)}m_buffer_intersection.shp" + ) + intersecting_polygons.to_file(admin_RE) + + styleTemplate = f"{current_script_path}/Style/Insights Aggregation.qml" + styleFileDestination = f"{workingDir}{Insights_folder}/{Insights_re_point}/" + styleFile = f"{admin_RE.split('.')[0]}.qml" + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + layer2 = QgsVectorLayer( + admin_RE, + f"{self.dlg.Buffer_Output_Field.text()}admin_unit_{str(bufferDistance)}m_buffer_intersection", + ) + QgsProject.instance().addMapLayer(layer2) + + layer = QgsVectorLayer( + shpOutput, + f"{self.dlg.Buffer_Output_Field.text()}{str(bufferDistance)}m_buffer", + ) + QgsProject.instance().addMapLayer(layer) + + self.dlg.REpoint_status.setText("RE point proximity complete!") + self.dlg.REpoint_status.repaint() + + def processCallback(self): + print("Call back") + + def walkabilityAggregate(self): + """ + This function is used in combination with the "walkability" function. + This function aggregates each of the active transport relating to + active transport into a single standardized raster file. + + Factors it is applied: + Accessibility Dimension + - Active transport + """ + self.dlg.ATAGG_status.setText("") + self.dlg.ATAGG_status.repaint() + # OUTPUT + current_script_path = os.path.dirname(os.path.abspath(__file__)) + workingDir = self.dlg.workingDir_Field.text() + os.chdir(workingDir) + Dimension = "Place Characterization" + AT_Folder = f"{Dimension}/AT" + + if os.path.exists(AT_Folder): + os.chdir(AT_Folder) + else: + pass + + rasOutput = self.dlg.AT_AGGOutput_Field.text() + + styleTemplate = f"{current_script_path}/Style/{Dimension}.qml" + styleFileDestination = f"{workingDir}{Dimension}/" + styleFile = f"{rasOutput.split('.')[0]}.qml" + + tif_list = [f for f in os.listdir(os.getcwd()) if f.endswith(".tif")] + raster_list = [] + + for ras in tif_list: + with rasterio.open(ras) as src: + raster_list.append(src.read(1)) + meta1 = src.meta + + len_raster_list = len(raster_list) + cumulative_sum = 0 + + for i in range(len_raster_list): + value = raster_list[i] + cumulative_sum += value + + aggregation = cumulative_sum / 4 + os.chdir("..") + + with rasterio.open(rasOutput, "w", **meta1) as dst: + dst.write(aggregation, 1) + + self.dlg.AT_Aggregate_Field.setText(f"{workingDir}{Dimension}/{rasOutput}") + + shutil.copy(styleTemplate, os.path.join(styleFileDestination, styleFile)) + + self.dlg.ATAGG_status.setText("Processing Complete!") + self.dlg.ATAGG_status.repaint() + + os.chdir(workingDir) + + + diff --git a/gender_indicator_tool/gender_indicator_tool_dialog.py b/gender_indicator_tool/gender_indicator_tool_dialog.py new file mode 100644 index 0000000..059d75f --- /dev/null +++ b/gender_indicator_tool/gender_indicator_tool_dialog.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + GenderIndicatorToolDialog + A QGIS plugin + Gender Indicator Tool + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2023-07-15 + git sha : $Format:%H$ + copyright : (C) 2023 by Pegasys + email : andre@pegasys.co.za + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" + +import os +import geopandas as gpd +import rasterio +from rasterio.features import rasterize +from rasterio.transform import from_origin + +from qgis.PyQt import uic +from qgis.PyQt import QtWidgets +from PyQt5.QtWidgets import QFileDialog + + + +# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer +FORM_CLASS, _ = uic.loadUiType(os.path.join( + os.path.dirname(__file__), 'gender_indicator_tool_dialog_base.ui')) + + +class GenderIndicatorToolDialog(QtWidgets.QDialog, FORM_CLASS): + def __init__(self, parent=None): + """Constructor.""" + super(GenderIndicatorToolDialog, self).__init__(parent) + # Set up the user interface from Designer through FORM_CLASS. + # After self.setupUi() you can access any designer object by doing + # self., and you can use autoconnect slots - see + # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html + # #widgets-and-dialogs-with-auto-connect + self.setupUi(self) diff --git a/gender_indicator_tool/gender_indicator_tool_dialog_base.ui b/gender_indicator_tool/gender_indicator_tool_dialog_base.ui new file mode 100644 index 0000000..7c9dcc4 --- /dev/null +++ b/gender_indicator_tool/gender_indicator_tool_dialog_base.ui @@ -0,0 +1,4355 @@ + + + GenderIndicatorToolDialogBase + + + + 0 + 0 + 900 + 775 + + + + + 900 + 900 + + + + Qt::DefaultContextMenu + + + Gender Enabling Environments Spatial Tool (GEEST) + + + + + + GEEST + + + false + + + false + + + + + + + 900 + 900 + + + + + + + 4 + + + + About + + + + + + true + + + + 16777215 + 16777215 + + + + + true + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOn + + + 0 + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#2b2b2b;">About</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#2b2b2b;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">GEEST effectively identifies areas where conditions are conducive for women to attain employment or business oportunitier by implementing the framework described in “A Methodological Framework for a Geospatial Assessment of Women’s Employment and Business Opportunities in the Renewable Energy Sector” which can be accessed in the tools GitHub repository.</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; color:#000000;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">For information on how to use the tool, consult the tool manual maintained in the tools GitHub repository. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14pt; font-weight:600; color:#2b2b2b;">Brief summary of the framework</span><span style=" font-size:8pt; color:#2b2b2b;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">The framework identifies 23 factors that are considered key to women’s ability to access employment opportunities. Due to data constraints only 21 of the 23 could be incorporated into the tool. These 21 factors are evaluated individually to create a single raster output layer for each factor with scores ranging from 0 to 5. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">Factor layers are aggregated into 4 dimensions (individual, contextual, accessibility and place characterization) using a multicriteria evaluation as described in the methodology document. Dimensions are also represented by a single raster layer with scores ranging from 0 to 5. Finally, dimensions can be further combined to produce a final aggregate output of scores ranging from 0 to 5. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">The range of scores within each layer can broadly be interpreted as follows: </span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<table border="1" style=" margin-top:0px; margin-bottom:0px; margin-left:16px; margin-right:0px; border-collapse:collapse;" cellspacing="2" cellpadding="0" bgcolor="transparent"> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-top:1px; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-top-style:solid; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; font-weight:600; color:#535353;"> Score range</span><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;"> </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-top:1px; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-top-style:solid; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; font-weight:600; color:#535353;"> Class</span><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;"> </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-top:1px; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-top-style:solid; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; font-weight:600; color:#535353;">Interpretation</span></p></td></tr> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-top:1px; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-top-style:solid; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">0.00 - 0.50 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-top:1px; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-top-style:solid; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">0 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-top:1px; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-top-style:solid; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">Not enabling </span></p></td></tr> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">0.51 - 1.50 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">1 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">Very low enabling </span></p></td></tr> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">1.51 - 2.50 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">2 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">Low enabling </span></p></td></tr> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">2.51 - 3.50 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">3 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">Moderately enabling</span></p></td></tr> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">3.51 - 4.50 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">4 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">Enabling </span></p></td></tr> +<tr> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-left:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid; border-left-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">4.51 - 5.00 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">5 </span></p></td> +<td bgcolor="transparent" style=" padding-left:0; padding-right:0; padding-top:0; padding-bottom:0; border-right:1px; border-bottom:1px; border-top-color:#000000; border-right-color:#000000; border-bottom-color:#000000; border-left-color:#000000; border-right-style:solid; border-bottom-style:solid;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; background-color:transparent;"><span style=" font-family:'Quire Sans','Quire Sans_EmbeddedFont','Quire Sans_MSFontService','sans-serif'; font-size:10pt; color:#535353;">Highly enabling </span></p></td></tr></table> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#2b2b2b;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14pt; font-weight:600; color:#2b2b2b;">Confidence Level</span><span style=" font-size:8pt; color:#2b2b2b;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">An overall confidence level is assigned to the final aggregate output based on the percentage of factors included in the aggregation. The level of confidence in the overall result can be interpreted as follows: </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">0-24% = Very low confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">25-49% = Low confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">50-74% = Medium confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">75-89% = High confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">90-100% = Very high confidence </span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; color:#2b2b2b;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14pt; font-weight:600; color:#2b2b2b;">Insights</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">The raw aggregate scores can be used in combination with information relating to the distribution of women and renewable energy sites to draw further insights. The insights tab groups population counts into three categories based on the lower, median, and upper quartile range of data to identify areas, of low, medium, and high numbers of women. These groupings are then combined with score classes to create 15 score-population classes listed below: </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">1 - Very low enablement, low population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">2 - Very low enablement, medium population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">3 - Very low enablement, high population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">4 - Low enablement, low population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">5 - Low enablement, medium population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">6 - Low enablement, high population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">7 - Moderately enabling, low population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">8 - Moderately enabling, medium population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">9 - Moderately enabling, high population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">10 - Enabling, low population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">11 - Enabling, medium population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">12 - Enabling, high population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">13 - Highly enabling, low population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">14 - Highly enabling, medium population </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;">15 - Highly enabling, high population </span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; color:#2b2b2b;"><br /></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; color:#2b2b2b;"><br /></p></body></html> + + + + + + + + Setup + + + + + + Set Ouput Directory + + + + + + + true + + + QFrame::NoFrame + + + QFrame::Sunken + + + 1 + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">GEEST - Setup</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">In this tab, the user must: </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 1</span><span style=" font-size:8pt;">: Set  the output directory. The output directory must be created by the user prior to setup and it is where all outputs from the tool will be stored; </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 2</span><span style=" font-size:8pt;">:</span><span style=" font-family:'Times New Roman'; font-size:8pt;"> </span><span style=" font-size:8pt;">Locate the boundary layer of the administrative level of analysis. The boundary layer must be in vector format (.shp); </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 3</span><span style=" font-size:8pt;">:</span><span style=" font-family:'Times New Roman'; font-size:8pt;"> </span><span style=" font-size:8pt;">Set the CRS by selecting the EPSG code for the region from the drop-down list. The recommended CRS for the tool to function optimally is the UTM Zone for the region.  A list of valid UTMs for SIDS can be found in the user manual; </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 4</span><span style=" font-size:8pt;">:</span><span style=" font-family:'Times New Roman'; font-size:8pt;">  </span><span style=" font-size:8pt;">Set the preferred resolution of the raster output. The default and recommended resolution for the raster output is 100m, but this can be altered if preferred. A higher resolution (smaller pixel) will produce more detailed outputs but will require more processing power. </span></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#535353;"> </span><span style=" font-size:24pt; font-weight:600; color:#535353;"> </span></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt; color:#535353;"> </span></p></body></html> + + + 80 + + + + + + + <html><head/><body><p>Full path to the working directory folder</p></body></html> + + + + + + + + + + ... + + + + + + + Set country boundary layer (Admin 0) + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Country Boundary</span></p></body></html> + + + SHP (*.shp) + + + + + + + Select appropriate CRS for the region (WGS 84 / UTM Zone) + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Co-ordinate Reference System</span></p></body></html> + + + + + + + Set preferred resolution of output in meters + + + + + + + 100000 + + + 100 + + + + + + + Comoros - EPSG: 32738 + + + + + + + Dominican Republic - EPSG: 32619 + + + + + + + Papua New Guinea - EPSG: 32755 + + + + + + + + Contextual + + + + + + 3 + + + + Workplace Discrimination + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Workplace Discrimination</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; font-weight:600; color:#535353;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab represents data at the national level and must be standardized on a scale ranging from 0 to 5. This indicator is composed by the Workplace Index score of the WBL 2024. The data is already formatted on scale from 1 to 100.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: The input field takes in the WBL index score.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates areas with a high level of workplace discrimination afforded to women, whereas a value of 0 indicates areas where no workplace discrimination are afforded to women. </span></p></body></html> + + + + + + + + + + + + + + + 200 + 50 + + + + User Input (WBL Index Score) + + + + + + + + 250 + 16777215 + + + + 100.000000000000000 + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + + 250 + 16777215 + + + + WD.tif + + + + + + + + + + + 250 + 16777215 + + + + Execute + + + + + + + + Regulatory Frameworks + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Regulatory Frameworks</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; font-weight:600; color:#535353;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab computes a raster containing a standardized measure of the regulatory frameworks afforded to women in the country of interest. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: The input fields take in the WBL Pay and Parenthood Index Scores percentage that represents the level of protective policies afforded to women. The calculation of this percentage is described in detail in the methodological framework document. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates areas with a high level of protective policies afforded to women, whereas a value of 0 indicates areas where no protective policies are afforded to women. </span></p></body></html> + + + + + + + + + + + + + + + 16777215 + 50 + + + + User Input (WBL Pay) + + + + + + + + 250 + 16777215 + + + + 100.000000000000000 + + + + + + + + 16777215 + 50 + + + + User Input (Parenthood Index Score) + + + + + + + + 250 + 16777215 + + + + 100.000000000000000 + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + + 250 + 16777215 + + + + RF.tif + + + + + + + + + + + 250 + 16777215 + + + + Execute + + + + + + + + Financial Inclusion + + + + + + + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Financial Inclusion</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab computes a raster containing a standardized measure of the percentage of women who have a bank account or have borrowed from a formal financial institution.   </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: The input field takes in the </span><a name="_Hlk146893042"></a><span style=" font-size:8pt;">p</span><span style=" font-size:8pt;">ercentage of women who have a bank account (WBL Entrepeneurship Index Score). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 signifies areas where all women possess a bank account or have borrowed from a formal financial institution, whereas  a value of 0 represents areas where no women have a bank account or have borrowed from a formal institution. </span></p></body></html> + + + + + + + + 300 + 50 + + + + User Input (WBL Entrepeneurship Index Score) + + + + + + + + 250 + 16777215 + + + + 100.000000000000000 + + + + + + + + 250 + 50 + + + + Raster Output Filename + + + + + + + + 250 + 16777215 + + + + FIN.tif + + + + + + + + + + + 250 + 16777215 + + + + Execute + + + + + + + + Aggregate + + + + + + Factors + + + + + + + Weight % + + + + + + + Workplace Discrimination (WD) + + + + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 33.340000000000003 + + + + + + + Regulatory Frameworks (RF) + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 33.329999999999998 + + + + + + + Financial Inclusion (FIN) + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 33.329999999999998 + + + + + + + Output Aggregate Raster Filename + + + + + + + Contextual_score.tif + + + + + + + + + + Execute + + + + + + + + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Contextual Factor Aggregation </span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab aggregates the raster outputs of all available factors within the contextual dimension by assuming equal weighting of all factors within the dimension. If equal weights are not appropriate for the specific context of the analysis, the user can adjust weights as necessary provided all weights still sum to 100%. If a factor was not calculated (perhaps due to missing data, or because it was deemed to be unimportant) that factor should be assigned a weight of 0%, and the remaining factor weights adjusted such that they add up to 100%. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Factor outputs that are created during the current working session are automatically loaded. Users can choose to load previously generated factor outputs from the output directory set up at tool initiation. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Output: A raster file containing values ranging from 0 to 5 is saved to the output directory and loaded into the workspace. Scores should be interpreted as outlined in the “About” tab.   </span></p></body></html> + + + + + + + Auto + + + + + + + + + + + + Accessibilty + + + + + + true + + + 4 + + + + Women's Travel Patterns + + + + + + + + + Execute + + + + + + + Facilty Point Layer Input (Green Spaces) + + + + + + + SHP (*.shp);;All files (*.*) + + + + + + + WTP.tif + + + + + + + + + + Facility Output Raster Filename + + + + + + + + + + + + + + Travel distance or time increments [meters OR min] + + + + + + + WTP_.tif + + + + + + + + + + 400, 800, 1200, 1500, 2000 + + + + + + + + + + false + + + + 16777215 + 50 + + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 2: Aggregate Facility Outputs</span></p></body></html> + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Women's Travel Patterns</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab estimates the ease with which women can access amenities related to their role as caregivers through a service area network anaysis facilitated using Openrouteservices' (ORS) isochrones service. Isochrones are derived from the OpenStreetMap (OSM) road network with the use of travel</span><span style=" font-size:7.8pt;"> </span><span style=" font-size:8pt;">distances or times as input to estimate ease of access to the input locations at five incrementally increasing measurement intervals. The largest interval being the maximum distance or time women would travel to reach these locations. The algorithm produces five catchment areas based on the road network. A scoring system ranging from 5 to 1 is assigned to the catchment area polygons, reflecting the decreasing order of accessibility. Areas outside these catchment areas receive a score of zero.</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 1</span><span style=" font-size:8pt;">: This step is repeated for each facility type. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: Point locations </span><a name="_Hlk146893610"></a><span style=" font-size:8pt;">(</span><span style=" font-size:8pt;">.shp) of amenities related to women's role as caregivers, including </span><a name="_Hlk146893640"></a><span style=" font-size:8pt;">c</span><span style=" font-size:8pt;">hildcare, primary and secondary schools, markets, grocery stores and recreational areas. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel mode</span><span style=" font-size:8pt;">: The user can select walking or driving as a travel mode and it is recommended that the same travel mode should be selected for all accessibility factors. The default travel mode is walking due to its inclusive nature. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Measurement</span><span style=" font-size:8pt;">: The default measurement for travel is distance in meters which is most appropriate for walking. If driving is selected as travel mode, time in minutes is a more appropriate measurement. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel increments</span><span style=" font-size:8pt;">: The travel increments for walking are loaded by default and based on evidence from literature. If evidence from the local context suggests alternative thresholds and increments are more appropriate, the user can alter these default values. If the selected travel mode is driving the equal measurement increments should be in minutes, and informed by the local context  (for example, if the user knows that the maximum time that women spend driving is 30 minutes, the increment input would be 6, 12, 18, 24, 30). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 2</span><span style=" font-size:8pt;">: The algorithm aggregates all raster layers created in step 1 by calculating the mean score for each pixel. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 identifies areas that are within the catchment defined by the smallest travel increment for all amenities (i.e., the areas where accessibility for women is highest). A value of 0 indicates areas that are outside of the catchment defined by the maximum travel increment for all amenities (i.e., no amenities are accessible to women from these areas). </span></p> +<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-style:italic;">Service Area Analysis is undertaken using the openrouteservice API and OpenStreetMap data. </span><span style=" font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;">© openrouteservice.org by HeiGIT | Road network data © OpenStreetMap contributors</span></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p></body></html> + + + + + + + + + + Select measurement + + + + + + + Facilty Point Layer Input (Grocery Stores) + + + + + + + Facilty Point Layer Input (Kindergarten or Primary Schools) + + + + + + + Select mode of travel + + + + + + + Aggregate + + + + + + + false + + + + 16777215 + 50 + + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 1: Input Facility Types</span></p></body></html> + + + + + + + + + + + + + + WTP Raster Output Filename + + + + + + + Facilty Point Layer Input (Pharmacies) + + + + + + + SHP (*.shp);;All files (*.*) + + + + + + + SHP (*.shp);;All files (*.*) + + + + + + + SHP (*.shp);;All files (*.*) + + + + + + + + Public Transport + + + + + + + 16777215 + 50 + + + + Transport Stops Input Layer (Point) + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Access to Public Transport</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab estimates the ease with which women can access public transport stops through a service area network anaysis facilitated using Openrouteservices' (ORS) isochrones service. Isochrones are derived from the OpenStreetMap (OSM) road network with the use of travel</span><span style=" font-size:7.8pt;"> </span><span style=" font-size:8pt;">distances or times as input to estimate ease of access to the public transport stops at five incrementally increasing measurement intervals. The largest interval being the maximum distance or time women would travel to reach these locations. The algorithm produces five catchment areas based on the road network. A scoring system ranging from 5 to 1 is assigned to the catchment area polygons, reflecting the decreasing order of accessibility. Areas outside these catchment areas receive a score of zero. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: Point locations (.shp) of public transport stops. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel mode</span><span style=" font-size:8pt;">: The user can select walking or driving as a travel mode and it is recommended that the same travel mode should be selected for all accessibility factors. The default travel mode is walking due to its inclusive nature. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Measurement</span><span style=" font-size:8pt;">: The default measurement for travel is distance in meters which is most appropriate for walking. If driving is selected as travel mode, time in minutes is a more appropriate measurement. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel</span><span style=" font-size:8pt;"> </span><span style=" font-size:8pt; font-weight:600;">increments</span><span style=" font-size:8pt;">: The travel increments for walking are loaded by default and based on evidence from literature. If evidence from the local context suggests alternative thresholds and increments are more appropriate, the user can alter these default values. If the selected travel mode is driving the equal measurement increments should be in minutes, and informed by the local context (for example, if the user knows that the maximum time that women spend driving is 30 minutes, the increment input would be 6, 12, 18, 24, 30). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 identifies areas that are within the catchment defined by the smallest travel increment for transport stops (i.e., the areas where transport accessibility for women is highest). A value of 0 indicates areas that are outside of the catchment defined by the maximum travel increment for transport stops (i.e., no transport stops are accessible to women from these areas). </span></p> +<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-style:italic;">Service Area Analysis is undertaken using the openrouteservice API and OpenStreetMap data. </span><span style=" font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;">© openrouteservice.org by HeiGIT | Road network data © OpenStreetMap contributors</span></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p></body></html> + + + + + + + + 16777215 + 50 + + + + + + + + + 16777215 + 50 + + + + Select mode of travel + + + + + + + + + + + 16777215 + 50 + + + + Select measurement + + + + + + + + + + + 16777215 + 50 + + + + Travel distance or time increments [meters OR min] + + + + + + + 250, 500, 750, 1000, 1250 + + + + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + PBT.tif + + + + + + + + + + Execute + + + + + + + + + + + + + + + Education and Training + + + + + + + 16777215 + 50 + + + + Education Facillities Input Layer (Point) + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Education and Training Facilities</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab estimates the ease with which women can access universities and technical training facilities through a service area network anaysis facilitated using Openrouteservices' (ORS) isochrones service. Isochrones are derived from the OpenStreetMap (OSM) road network with the use of travel</span><span style=" font-size:7.8pt;"> </span><span style=" font-size:8pt;">distances or times as input to estimate ease of access to the education facilities at five incrementally increasing measurement intervals. The largest interval being the maximum distance or time women would travel to reach these locations. The algorithm produces five catchment areas based on the road network. A scoring system ranging from 5 to 1 is assigned to the catchment area polygons, reflecting the decreasing order of accessibility. Areas outside these catchment areas receive a score of zero.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: Point locations (.shp) of universities and technical training facilities.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel mode</span><span style=" font-size:8pt;">: The user can select walking or driving as a travel mode and it is recommended that the same travel mode should be selected for all accessibility factors. The default travel mode is walking due to its inclusive nature.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Measurement</span><span style=" font-size:8pt;">: The default measurement for travel is distance in meters which is most appropriate for walking. If driving is selected as travel mode, time in minutes is a more appropriate measurement. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel increments</span><span style=" font-size:8pt;">: The travel increments for walking are loaded by default and based on evidence from literature. If evidence from the local context suggests alternative thresholds and increments are more appropriate, the user can alter these default values. If the selected travel mode is driving the equal measurement increments should be in minutes, and informed by the local context (for example, if the user knows that the maximum time that women spend driving is 30 minutes, the increment input would be 6, 12, 18, 24, 30). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 identifies areas that are within the catchment defined by the smallest travel increment for an education facility (i.e., the areas where access to education facilities for women is highest). A value of 0 indicates areas that are outside of the catchment defined by the maximum travel increment for education facilities (i.e., no education facilities are accessible to women from these areas).</span><span style=" font-size:7.8pt;"> </span></p> +<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-style:italic;">Service Area Analysis is undertaken using the openrouteservice API and OpenStreetMap data. </span><span style=" font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;">© openrouteservice.org by HeiGIT | Road network data © OpenStreetMap contributors</span></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p></body></html> + + + + + + + + 16777215 + 50 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 16777215 + 50 + + + + Select mode of travel + + + + + + + + + + + 16777215 + 50 + + + + Select measurement + + + + + + + + + + + 16777215 + 50 + + + + Travel distance or time increments [meters OR min] + + + + + + + 2000, 4000, 6000, 8000, 10000 + + + + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + ETF.tif + + + + + + + + + + Execute + + + + + + + + + + + + + + + Health Facilities + + + + + + + 16777215 + 50 + + + + Health Facillities Input Layer (Point) + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Health Facilities</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This algorithm estimates the ease with which women can access health facilities such as hospitals and women's health clinics through a service area network anaysis facilitated using Openrouteservices' (ORS) isochrones service. Isochrones are derived from the OpenStreetMap (OSM) road network with the use of travel</span><span style=" font-size:7.8pt;"> </span><span style=" font-size:8pt;">distances or times as input to estimate ease of access to health facilities at five incrementally increasing measurement intervals. The largest interval being the maximum distance or time women would travel to reach these locations. The algorithm produces five catchment areas based on the road network. A scoring system ranging from 5 to 1 is assigned to the catchment area polygons, reflecting the decreasing order of accessibility. Areas outside these catchment areas receive a score of zero.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: Point locations (.shp) of health facilities such as hospitals and women's health clinics.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel mode</span><span style=" font-size:8pt;">: The user can select walking or driving as a travel mode and it is recommended that the same travel mode should be selected for all accessibility factors. The default travel mode is walking due to its inclusive nature.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Measurement</span><span style=" font-size:8pt;">: The default measurement for travel is distance in meters which is most appropriate for walking. If driving is selected as travel mode, time in minutes is a more appropriate measurement. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel increments</span><span style=" font-size:8pt;">: The travel increments for walking are loaded by default and based on evidence from literature. If evidence from the local context suggests alternative thresholds and increments are more appropriate, the user can alter these default values. If the selected travel mode is driving the equal measurement increments should be in minutes, and informed by the local context (for example, if the user knows that the maximum time that women spend driving is 30 minutes, the increment input would be 6, 12, 18, 24, 30). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 identifies areas that are within the catchment defined by the smallest travel increment for a health facility (i.e., the areas where access to health facilities for women is highest). A value of 0 indicates areas that are outside of the catchment defined by the maximum travel increment for health facilities (i.e., no health facilities are accessible to women from these areas).</span><span style=" font-size:7.8pt;"> </span></p> +<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-style:italic;">Service Area Analysis is undertaken using the openrouteservice API and OpenStreetMap data. </span><span style=" font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;">© openrouteservice.org by HeiGIT | Road network data © OpenStreetMap contributors</span></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p></body></html> + + + + + + + + 16777215 + 50 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 16777215 + 50 + + + + Select mode of travel + + + + + + + + + + + 16777215 + 50 + + + + Select measurement + + + + + + + + + + + 16777215 + 50 + + + + Travel distance or time increments [meters OR min] + + + + + + + 2000, 4000, 6000, 8000, 10000 + + + + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + HEF.tif + + + + + + + + + + Execute + + + + + + + + + + + + + + + Finance Facilities + + + + + + + 16777215 + 50 + + + + Financial Facillities Input Layer (Point) + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Financial Facilities</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This algorithm estimates the ease with which women can access financial facilities such as banks and ATMs through a service area network anaysis facilitated using Openrouteservices' (ORS) isochrones service. Isochrones are derived from the OpenStreetMap (OSM) road network with the use of travel</span><span style=" font-size:7.8pt;"> </span><span style=" font-size:8pt;">distances or times as input to estimate ease of access to financial facilities at five incrementally increasing measurement intervals. The largest interval being the maximum distance or time women would travel to reach these locations. The algorithm produces five catchment areas based on the road network. A scoring system ranging from 5 to 1 is assigned to the catchment area polygons, reflecting the decreasing order of accessibility. Areas outside these catchment areas receive a score of zero. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: Point locations (.shp) of financial facilities such as banks and ATMs. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel mode</span><span style=" font-size:8pt;">: The user can select walking or driving as a travel mode and it is recommended that the same travel mode should be selected for all accessibility factors. The default travel mode is walking due to its inclusive nature. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Measurement</span><span style=" font-size:8pt;">: The default measurement for travel is distance in meters which is most appropriate for walking. If driving is selected as travel mode, time in minutes is a more appropriate measurement. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Travel increments</span><span style=" font-size:8pt;">: The travel increments for walking are loaded by default and based on evidence from literature. If evidence from the local context suggests alternative thresholds and increments are more appropriate, the user can alter these default values. If the selected travel mode is driving the equal measurement increments should be in minutes, and informed by the local context (for example, if the user knows that the maximum time that women spend driving is 30 minutes, the increment input would be 6, 12, 18, 24, 30). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 identifies areas that are within the catchment defined by the smallest travel increment for a financial facility (i.e., the areas where access to financial facilities for women is highest). A value of 0 indicates areas that are outside of the catchment defined by the maximum travel increment for financial facilities (i.e., no financial facilities are accessible to women from these areas). </span></p> +<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-style:italic;">Service Area Analysis is undertaken using the openrouteservice API and OpenStreetMap data. </span><span style=" font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;">© openrouteservice.org by HeiGIT | Road network data © OpenStreetMap contributors</span></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p> +<p align="center" style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:8pt; font-style:italic;"><br /></p></body></html> + + + + + + + + 16777215 + 50 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 16777215 + 50 + + + + Select mode of travel + + + + + + + + + + + 16777215 + 50 + + + + Select measurement + + + + + + + + + + + 16777215 + 50 + + + + Travel distance or time increments [meters OR min] + + + + + + + 400, 800, 1200, 2000, 3000 + + + + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + FIF.tif + + + + + + + + + + Execute + + + + + + + + + + + + + + + Aggregate + + + + + + Factors + + + + + + + Weight % + + + + + + + Women's Travel Patterns (WTP) + + + true + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 20.000000000000000 + + + + + + + Public Transport (PBT) + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 20.000000000000000 + + + + + + + Education and Training (ETF) + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 20.000000000000000 + + + + + + + Health Facilities (HEA) + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 20.000000000000000 + + + + + + + Financial Facilities (FIF) + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 20.000000000000000 + + + + + + + Output Aggregate Raster Filename + + + + + + + Accessibility_score.tif + + + + + + + + + + Execute + + + + + + + + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Accessibility Factor Aggregation </span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab aggregates the raster outputs of all available factors within the accessibility dimension by assuming equal weighting of all factors within the dimension. If equal weights are not appropriate for the specific context of the analysis, the user can adjust weights as necessary provided all weights still sum to 100%. If a factor was not calculated (perhaps due to missing data, or because it was deemed to be unimportant) that factor should be assigned a weight of 0%, and the remaining factor weights adjusted such that they add up to 100%. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Factor outputs that are created during the current working session are automatically loaded. Users can choose to load previously generated factor outputs from the output directory set up at tool initiation. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory and loaded into the workspace. Scores should be interpreted as outlined in the “About” tab.   </span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; color:#535353;"><br /></p></body></html> + + + + + + + Auto + + + + + + + + + + + + Place Characterization + + + + + + 0 + + + + Active Transport + + + + + + + 350 + 25 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 16777215 + 50 + + + + Cycle Paths Input Layer (Polyline) + + + + + + + + 16777215 + 50 + + + + Subfactor Output Filename + + + + + + + + 150 + 25 + + + + AT_.tif + + + + + + + + + + + 350 + 25 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 150 + 16777215 + + + + Execute + + + + + + + + 150 + 16777215 + + + + Aggregate + + + + + + + + 250 + 50 + + + + Raster Output Filename + + + + + + + + + + + + + + + 350 + 25 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 16777215 + 50 + + + + Crosswalks Input Layer (Point) + + + + + + + + 350 + 25 + + + + SHP (*.shp);;All files (*.*) + + + + + + + + 150 + 25 + + + + Activetransport.tif + + + + + + + + + + + + + + + + + true + + + + 400 + 1000 + + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Active Transport</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab defines areas based on the degree to which they support safe and efficient active transport (walking, cycling and other forms of non-motorized transport) for women, through the classification of road type.</span><span style=" font-size:7.8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: A road network polyline layer (.shp) such as cycle paths, and pathways.</span><span style=" font-size:7.8pt;"> A point layer (.shp) such as crosswalks points. A polygon (.shp) for blocks.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Process</span><span style=" font-size:8pt;">: The user inputs all the required layers. The territory is then divided into 100m by 100m rasters.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If there:</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">- No features in a raster cell, a value of 0 is assigned.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">- Is 1 feature in a raster cell, a value of 3 is assigned.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">- Are 2 or more features in a raster cell, a value of 5 is assigned.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates areas that are most conducive to active transport. A value of 3 indicates areas that are least conducive to active transport. A value of 0 indicates areas where there are no roads at all, and thus active transport is assumed to be impossible.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html> + + + + + + + + 16777215 + 50 + + + + Block Input Layer (Polygon) + + + + + + + + 300 + 50 + + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 1: Input Layers</span></p></body></html> + + + + + + + + 16777215 + 50 + + + + Pathways Input Layer (Polyline) + + + + + + + + 300 + 50 + + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 2: Aggregate Active Transport Inputs</span></p></body></html> + + + + + + + + Safety + + + + + + + 16777215 + 50 + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer</span></p></body></html> + + + TIF (*.tif);;SHP (*.shp);;All files (*.*) + + + + + + + + 16777215 + 50 + + + + Input Layer (Point, Polygon or Raster) + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + + 0 + 0 + + + + Field of interest in polygon layer + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Geoprocessiong Execution</span></p></body></html> + + + Execute + + + + + + + true + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Safe Urban Design</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab defines areas based on how brightly lit they are and assumes that brightly lit areas are safer than areas with no lights.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: Streetlight Locations (.shp, Point) or, if unavailable, VIIRS Nighttime Lights dataset (.tif) may be used as proxy data for streetlight locations. Alternatively, Perceived Safety data (.shp, Polygon) can be used if other data is unavailable. (</span><span style=" font-size:8pt; font-weight:600; font-style:italic;">Important note</span><span style=" font-size:8pt;">: If used for assessing safe urban design, nighttime lights should be excluded from the calculation of Electrical access.)</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates the most brightly lit areas and by assumption the safest areas. 0 represents areas with no nighttime lights and by assumption the least safe areas.</span></p></body></html> + + + + + + + false + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Standardized Raster Output</span></p></body></html> + + + SAF.tif + + + false + + + + + + + + + + + 0 + 0 + + + + Score level (0-5) + + + + + + + + + + + + + + + 0 + 0 + + + + Perceived Safety Value + + + + + + + + Digital Inclusion + + + + + + Execute + + + + + + + + + + + + + + + 16777215 + 50 + + + + Output Raster Filename + + + + + + + + 250 + 16777215 + + + + DIG.tif + + + + + + + + + + + 250 + 50 + + + + Field of interest in the polygon layer + + + + + + + + 250 + 50 + + + + Polygon Input Layer + + + + + + + + + + + 350 + 50 + + + + Internet Access Value + + + + + + + + 250 + 16777215 + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer</span></p></body></html> + + + SHP (*.shp);;All files (*.*) + + + + + + + + 250 + 16777215 + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer Field</span></p></body></html> + + + + + + + true + + + + 16777215 + 1200 + + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Digital Inclusion</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab characterizes areas based on the percentage of houses with access to the Internet. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: A polygon (.shp) with the value or a value representing the percentage of houses with Internet access (The Internet Access value can be used if there is no polygon input). </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates areas where all houses have access to the Internet while 0 indicates areas where no houses have Internet access. </span></p></body></html> + + + + + + + Set + + + + + + + + Environment Hazards + + + + + + + 16777215 + 50 + + + + Drought Input Layer (Raster) + + + + + + + + + + + + + + + 16777215 + 50 + + + + Landslide Input Layer (Raster) + + + + + + + + 16777215 + 50 + + + + Tropical cyclone Input Layer (Raster) + + + + + + + + 250 + 50 + + + + <html><head/><body><p><span style=" font-weight:600;">Geoprocessiong Execution</span></p></body></html> + + + Aggregate + + + + + + + + + + + + + + + 300 + 50 + + + + + + + + + 300 + 50 + + + + + + + + + 16777215 + 50 + + + + Fire Hazard Input Layer (Raster) + + + + + + + + 300 + 50 + + + + + + + + + 250 + 50 + + + + Hazard_.tif + + + + + + + + + + false + + + + 350 + 50 + + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Step 2: Aggregate Natural Disaster Risk</span></p></body></html> + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Environmental Hazards</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab characterizes areas based on their vulnerability to natural disasters. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Input</span><span style=" font-size:8pt;">: One or many polygon layers (.shp) which contain a field representing the level of risk of natural disasters. Risk should be classed as either low, medium or high. Different classes of natural disasters (such as landslide risk or flood risk) are often represented by separate input layers, and so each type of natural disaster is processed individually in step 1, before aggregating outputs in step 2. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 1 </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">For each layer representing a different class of natural disaster, the input layer is set, and the user selects the field containing the level of risk from the drop-down list. In each layer, areas are classified as having either low, medium, or high risk. The algorithm reclassifies the input field such that low-risk areas are assigned a score of 5, medium-risk areas are assigned a score of 2, and high-risk areas are assigned a score of 0. The reclassified data are rasterized. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 2</span><span style=" font-size:8pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">The algorithm identifies all raster layers created in step 1 and aggregates them by calculating the mean score for each pixel. The default raster output filename does not need to be altered unless the user wishes. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates areas where natural disaster risk is lowest. A value of 0 indicates areas where natural disaster risk is highest. </span></p></body></html> + + + + + + + + 300 + 50 + + + + + + + + + 200 + 50 + + + + Execute + + + + + + + false + + + + 350 + 50 + + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Step 1: Repeat for all available disaster risk types</span></p></body></html> + + + + + + + + 16777215 + 50 + + + + Aggregated Hazard Raster Output Filename + + + + + + + + 300 + 50 + + + + + + + + + 250 + 50 + + + + <html><head/><body><p><span style=" font-weight:600;">Standardized Raster Output</span></p></body></html> + + + ENV.tif + + + + + + + + + + + 16777215 + 50 + + + + Flood Hazard Input Layer (Raster) + + + + + + + + 16777215 + 50 + + + + Hazard Type Output Raster Filename + + + + + label_128 + ENV_Output_Field + ENV_Execute_PB + label_130 + ENV_Input_Field + ENV_Aggregate_PB + ENV_AGGOutput_Field + textEdit_30 + label_133 + textEdit_40 + textEdit_32 + ENV_status + ENVAGG_status + ENV_Input_Field_2 + label_131 + ENV_Input_Field_3 + ENV_Input_Field_4 + label_132 + label_134 + ENV_Input_Field_5 + label_127 + + + + Education + + + + + + + 16777215 + 50 + + + + Polygon Input Layer + + + + + + + + 16777215 + 50 + + + + Raster Output Filename + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Education</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab computes a raster containing a standardized measure of the percentage of women who have achieved a post-secondary education in the country of interest.   </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="_Hlk146891458"></a><span style=" font-size:8pt; font-weight:600;">I</span><span style=" font-size:8pt; font-weight:600;">nput</span><span style=" font-size:8pt;">: A Polygon layer (.shp) containing a field reporting the percentage of women who have achieved a post-secondary education. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. A value of 5 indicates regions where all women have completed post-secondary education, whereas a value of 0 indicates areas where no women have attained post-secondary education. </span></p></body></html> + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer</span></p></body></html> + + + SHP (*.shp);;All files (*.*) + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer Field</span></p></body></html> + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Standardized Raster Output</span></p></body></html> + + + EDU.tif + + + + + + + + + + + 16777215 + 50 + + + + Field of interest in the polygon layer + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Geoprocessiong Execution</span></p></body></html> + + + Execute + + + + + + + + + + + + + + + 0 + 0 + + + + Education Level Value + + + + + + + + Fragility, conflict, and violence(FCV) + + + + Raster Output Filename + + + false + + + + + <html><head/><body><p><span style=" font-weight:600;">Standardized Raster Output</span></p></body></html> + + + FCV.tif + + + + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Fragility, conflict, and violence</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This indicator is structured by assigning scores to rasters based on their overlap with buffers indicating different types of events.  </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="_Hlk146891458"></a><span style=" font-size:8pt; font-weight:600;">I</span><span style=" font-size:8pt; font-weight:600;">nput</span><span style=" font-size:8pt;">: ACLED data as a csv file (.csv). If the impact radius of an event is known, it should be used instead.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. Regions with score of 0 overlap with bufers of serious events. Regions with a score of 5 do not overlap with buffers of any event/s.<br /><br /></span><span style=" font-size:8pt; font-weight:600;">Warning</span><span style=" font-size:8pt;">: The processing may take a very long time to complete, during which the interface might become unresponsive. </span></p></body></html> + + + + + + + + 200 + 50 + + + + Raster Output Filename + + + false + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Geoprocessiong Execution</span></p></body></html> + + + Execute + + + + + + + + 200 + 50 + + + + + + + + + 16777215 + 50 + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer</span></p></body></html> + + + CSV (*.csv);;All files (*.*) + + + + + + + + 200 + 50 + + + + Input CSV File + + + + + + + + + + + + + + + 350 + 50 + + + + Impact Radius in Metres (Optional) + + + + + + + 100000.000000000000000 + + + + + + + + Water sanitation + + + + + + true + + + + 400 + 2000 + + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Water and Sanitation</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab computes a raster containing a standardized measure of the percentage of women who have achieved water and sanitation services in the country of interest.   </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="_Hlk146891458"></a><span style=" font-size:8pt; font-weight:600;">I</span><span style=" font-size:8pt; font-weight:600;">nput</span><span style=" font-size:8pt;">: A Point layer (.shp).</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Process</span><span style=" font-size:8pt;">: The user inputs the water point layers. The territory is then divided into 100m by 100m rasters.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If there:</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">- No features in a raster cell, a value of 0 is assigned.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">- Is 1 feature in a raster cell, a value of 3 is assigned.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">- Are 2 or more features in a raster cell, a value of 5 is assigned.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output</span><span style=" font-size:8pt;">: A raster file containing values ranging from 0 to 5 is saved to the output directory. Regiosn with no waater points will have a value of 0, regions with one water point will have a value of 3, while regions with two or more water points will hava value of 5.</span></p></body></html> + + + + + + + + 350 + 50 + + + + Raster Output Filename + + + + + + + + 250 + 16777215 + + + + <html><head/><body><p><span style=" font-weight:600;">Standardized Raster Output</span></p></body></html> + + + WAS.tif + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Geoprocessiong Execution</span></p></body></html> + + + Execute + + + + + + + + + + + + + + + 350 + 50 + + + + Point Input Layer + + + + + + + + 0 + 0 + + + + + 350 + 50 + + + + <html><head/><body><p><span style=" font-weight:600;">Education Facility Point Layer</span></p></body></html> + + + SHP (*.shp);;All files (*.*) + + + + + + + + Aggregate + + + + + + + 16777215 + 50 + + + + Factors + + + + + + + + + + + + + + Execute + + + + + + + + + + + + + + 100.000000000000000 + + + 14.300000000000001 + + + + + + + 100.000000000000000 + + + 14.300000000000001 + + + + + + + ... + + + + + + + 100.000000000000000 + + + 14.300000000000001 + + + + + + + Safe Urban Design (SAF) + + + + + + + Environment Hazards (ENV) + + + + + + + + + + + + + + + + + + + + + ... + + + + + + + Digital Inclusion (DIG) + + + + + + + Water and Sanitation (WAS) + + + + + + + 100.000000000000000 + + + 14.199999999999999 + + + + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 14.300000000000001 + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Place Characterization Factor Aggregation </span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab aggregates the raster outputs of all available factors within the place characterization dimension by assuming equal weighting of all factors within the dimension. If equal weights are not appropriate for the specific context of the analysis, the user can adjust weights as necessary provided all weights still sum to 100%. If a factor was not calculated (perhaps due to missing data, or because it was deemed to be unimportant) that factor should be assigned a weight of 0%, and the remaining factor weights adjusted such that they add up to 100%. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Factor outputs that are created during the current working session are automatically loaded. </span><span style=" font-family:'Calibri','sans-serif'; font-size:9pt;">Users can choose to load previously generated factor outputs from the output directory set up at tool initiation.</span><span style=" font-size:9pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Calibri','sans-serif'; font-size:8pt; font-weight:600;">Output</span><span style=" font-family:'Calibri','sans-serif'; font-size:8pt;">: </span><span style=" font-size:8pt;">A raster file containing values ranging from 0 to 5 is saved to the output directory and loaded into the workspace. Scores should be interpreted as outlined in the “About” tab.</span><span style=" font-size:7.8pt;"> </span></p></body></html> + + + + + + + + + + + + + + ... + + + + + + + ... + + + + + + + 100.000000000000000 + + + 14.300000000000001 + + + + + + + ... + + + + + + + Place_score.tif + + + + + + + + + + Education (EDU) + + + + + + + 100.000000000000000 + + + 14.300000000000001 + + + + + + + + + + + + + + ... + + + + + + + Fragility, conflict and violence (FCV) + + + + + + + Active Transport (Activetransport) + + + + + + + + 16777215 + 50 + + + + Weight % + + + + + + + + + + + + + + + 16777215 + 50 + + + + Output Aggregate Raster File + + + + + + + Auto + + + + + + + + + + + + Aggregate + + + + + + Execute + + + + + + + + + + + + + + ... + + + + + + + Contextual + + + + + + + + 16777215 + 50 + + + + Weighting + + + + + + + + + + + + + + ... + + + + + + + 100.000000000000000 + + + 33.340000000000003 + + + + + + + 100.000000000000000 + + + 33.329999999999998 + + + + + + + Accessibility + + + + + + + true + + + + MS Shell Dlg 2 + + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#515151;">Dimension Aggregation </span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#515151;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">This tab aggregates the computed raster outputs of all available dimensions. By default, it assigns double the weight to Place Characterization and Accessibility dimensions compared to Contextual and Individual Dimensions, reflecting the spatial emphasis of the tool.</span><span style=" font-size:7.8pt; color:#515151;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">Dimension aggregation outputs that are created during the current working session are automatically loaded. </span><span style=" font-family:'Calibri','sans-serif'; font-size:9pt; color:#515151;">Users can choose to load previously generated dimension aggregation outputs from the output directory set up at tool initiation.</span><span style=" font-size:9pt; color:#515151;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">If data for any dimension is missing, the user must assign a weight of zero to that dimension and adjust weights for other dimensions to ensure that they add up to 100%. </span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; color:#515151;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600; color:#515151;">Confidence Level</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">A confidence level is reported based on the percentage of factors included in the aggregation. The level of confidence can be interpreted as follows: </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">0-24% = Very low confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">25-49% = Low confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">50-74% = Medium confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">75-89% = High confidence </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; color:#515151;">90-100% = Very high confidence </span></p></body></html> + + + + + + + 100.000000000000000 + + + 33.329999999999998 + + + + + + + + 16777215 + 50 + + + + Output Aggregate Raster File + + + + + + + Place Characterization + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + 16777215 + 50 + + + + Dimensions + + + + + + + + + + + + + + WEE.tif + + + + + + + + + + Auto + + + + + + + + Insights + + + + + + true + + + 0 + + + + Enablement + + + + + + false + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 1: Classify into discrete classes</span></p></body></html> + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Insights - Enablement</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab enables the user to obtain further refined insights from the score rasters produced in the previous tabs based on discrete descriptive classes. Population data can also be combined with the discrete score output dataset, allowing for more nuanced insights that account for both the level of enablement and population. Additionally, this tab provides the option to aggregate results based on specific boundaries of interest.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Step 1: Classify into discrete classes</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Enablement Score Input layer</span><span style=" font-size:8pt;"> (required): </span><span style=" font-size:8pt; color:#0e101a; background-color:transparent;">The dimension or final weighted aggregated score layer of interest (.tif). Scores are grouped into five discrete classes as defined in the 'About' Tab.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output (Level of Enablment): </span><span style=" font-family:'Times New Roman'; font-size:8pt;"> </span><span style=" font-size:8pt; color:#0e101a; background-color:transparent;">The enablement score input layer classified into five discrete level of enablement classes.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Population Input layer</span><span style=" font-size:8pt;"> (required): A p</span><span style=" font-size:8pt; color:#0e101a; background-color:transparent;">opulation count layer (.tif). This should be represented as the count of women in each raster cell.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output (Relative Population Count): </span><span style=" font-size:8pt; color:#0e101a; background-color:transparent;">The population input layer classified into three discrete classes based on the lower quartile range, interquartile range, and upper quartile range of data to identify areas of relatively low, medium, and high population per region.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Calibri','sans-serif'; font-size:10pt;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Step 2: Combine score and population classifications</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Level of Enablment Input Layer: </span>The level of enablement output raster layer produced in step 1.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Relative Population Count Input Layer: </span>The relative population count raster layer produced in step 1.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Output (Combine Classification): </span><span style=" font-family:'Times New Roman'; font-size:8pt;"> </span><span style=" font-size:8pt;">A single raster layer which combines the five levels of enablement classes and three population classes. This layer contains 15 classes defined in the ‘About’ tab. This output covers the entire country or region of interest. </span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Step 3: Aggregation</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Combine Classification Input Layer: </span>The combine classification output raster layer produced in step 2.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Aggregation Input Layer: </span><span style=" font-size:8pt;">A polygon layer representing boundaries of interest for aggregation (e.g. municipal boundary layer) </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output (Aggregation): </span><span style=" font-size:8pt;">A polygon layer showing the 15 score-population classes produced in step 2 aggregated to the scale of the aggregation input layer using the majority class. </span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + + + Enablement Score Input layer (Raster) + + + + + + + + + + Classify + + + + + + + + + + + + + + Population Input Layer (Raster) + + + + + + + + + + Classify + + + + + + + + + + + + + + false + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 2: Combine score and population classifications</span></p></body></html> + + + + + + + Level of Enablement Input layer (Raster) + + + + + + + + + + Relative Population Count Input Layer (Raster) + + + + + + + + + + Combine Classification + + + + + + + + + + + + + + false + + + QFrame::NoFrame + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Step 3: Aggregation</span></p></body></html> + + + + + + + Combined Classification Input Layer (Raster) + + + + + + + + + + Aggregation Input Layer (Polygon) + + + + + + + + + + Output Aggregated File (Polygon) + + + + + + + WEE_pop_adm_score + + + + + + + + + + Execute + + + + + + + + + + + + + + + RE Zone Raster Locations + + + + + + Combined Classification Input Layer (Raster) + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Insights - Raster Locations</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab enables the user to obtain further insights from the results based on proximity to potential RE zones. The outputs from this tab are assigned the combine classification score to the RE zones input raster and extract the aggregated polygon/admin units that intersect with RE zones.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Combine Classification Input Layer:</span><span style=" font-size:8pt;"> </span>The combine classification output raster layer produced in step 2 of the &quot;Enablement&quot; tab.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Aggregated Combine Classification Input Layer: </span>The aggregated combine classification output polygon layer produced in step 3 of the &quot;Enablement&quot; tab.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Potential RE Zones Input Layer: </span><span style=" font-size:8pt;">A raster layer highlighting the potetial RE zones. Pixels of zones in the region that have no RE potential needs to be represented with &quot;no data&quot; or &quot;inf&quot; values in the raster file.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output (RE zones): </span><span style=" font-size:8pt;"> A raster layer of the RE zones with the combine classification scores assigned to the highlited RE zones. Additionally, the aggregated polygon/admin units that intersect with RE zones are extracted along with their majority combine classification class to a new polygon layer.</span></p></body></html> + + + + + + + + + + Aggregated Combined Classification Input Layer (Polygon) + + + + + + + + + + Potential RE Zones Input Layer (Raster) + + + + + + + + + + RE Zones Output File/s + + + + + + + AOI_WEE_score + + + + + + + + + + Execute + + + + + + + + + + + + + + + RE Point Locations + + + + + + Combined Classification Layer (Raster) + + + + + + + true + + + QFrame::NoFrame + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600; color:#535353;">Insights - Point Locations</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:20pt; font-weight:600; color:#535353;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This tab enables the user to obtain further insights from the results based on proximity to RE point locations. The first output from this tab aggregates the combine classification score based on the majority class that falls within the RE point location buffers. The second output extracts the aggregated polygon/admin units that intersect with RE point location buffers. The majority class extraction for the buffers is conducted using the original combine classification raster produced in step 2 of the &quot;Enablement&quot; tab.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Combine Classification Input Layer:</span><span style=" font-size:8pt;"> </span>The combine classification output raster layer produced in step 2 of the <span style=" font-size:8pt;"> &quot;Enablement&quot;</span> tab.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Aggregated Combine Classification Input Layer: </span>The aggregated combine classification output polygon layer produced in step 3 of the <span style=" font-size:8pt;"> &quot;Enablement&quot;</span> tab.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">RE Point Location Input Layer: </span><span style=" font-size:8pt;">Point locations of interest (.shp). This could be existing RE job locations or other points of interest.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Buffer Distance: </span><span style=" font-size:8pt;">Maximum radial distance of circular buffer from point location in meters.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Output (RE Point): </span><span style=" font-size:8pt;">A polygon layer showing the 15 score-population classes aggregated to the RE point buffers scale using the majority class that fall within the buffers. Additionally, the aggregated polygon/admin units that intersect with RE point location buffers are extracted along with their majority class to a new polygon layer.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html> + + + + + + + + + + Aggregated Combined Classification Layer (Polygon) + + + + + + + + + + RE Point Location Input Layer (Point) + + + + + + + + + + Set buffer distance in meters + + + + + + + 100000 + + + 10000 + + + + + + + RE Point Location Output File/s + + + + + + + POI_WEE_score + + + + + + + + + + Execute + + + + + + + + + + + + + + + + + + + + + + + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
+ + QgsFileWidget + QWidget +
qgsfilewidget.h
+
+ + QgsFilterLineEdit + QLineEdit +
qgsfilterlineedit.h
+
+ + QgsProjectionSelectionWidget + QWidget +
qgsprojectionselectionwidget.h
+
+ + QgsSpinBox + QSpinBox +
qgsspinbox.h
+
+
+ + +
diff --git a/gender_indicator_tool/icon.png b/gender_indicator_tool/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f169fe0540edf31938006417704592dafdc149d8 GIT binary patch literal 1067 zcmV+`1l0S9P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf1Hef{K~y+TeU*Dm z+f@|Dzojh&e(V7u;DlM&z!A1Tl&J(XDnT-~3~^iHB4$1^rv9N@qHb}JP0iv^_K;~z zHXFJGHkK_YnFvHT_hJw&M!N|*VQE^{vPsLM?Fzrr$MM(uyKBp+ev;;#^S$?;b8_x& zs~`#d2jZ*`htBrmf_Va?5b)6Q9E_@xu`ES{)#(YS&PqV8pya#f6UXg5-l*3jv3v)d zq90X-xmZ(>36DYzjY^3OK?Tzc;kd<%V>d-n2nV;$E!NBkcuKYTPVBc|k+j|}R+b-j$ zq1(vR5Y}lIvVCJ>fZ6891su2`!hAO%X@ozg0-5;Vfc6$NPUXgqpe+pw5O?jOD&7fsU59$hHnwySR&Jzs4mPtm7XSNa5#oUE8?<#_g~c|8S)U(^7YcYF lEd?7YO0ctL6W?$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/gender_indicator_tool/resources.py b/gender_indicator_tool/resources.py new file mode 100644 index 0000000..b80173d --- /dev/null +++ b/gender_indicator_tool/resources.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created by: The Resource Compiler for PyQt5 (Qt v5.15.2) +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore + +qt_resource_data = b"\ +\x00\x00\x04\x2b\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x17\x00\x00\x00\x12\x08\x06\x00\x00\x00\xb0\xe7\x45\x13\ +\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\x00\x00\x00\ +\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\ +\x0e\x1b\x00\x00\x03\xc0\x49\x44\x41\x54\x38\x4f\x7d\x95\x7b\x4c\ +\xdb\x55\x14\xc7\xbf\xa5\x2d\x05\x7e\xec\x01\x21\xe0\x84\x59\xd8\ +\xc0\x11\xb6\x3f\x94\xa9\x04\x34\x2a\x41\x32\xb6\x0c\x71\x5b\xe2\ +\x22\x66\x3e\x32\xa6\xfe\xa1\x5b\xa2\x6e\x71\x90\x4d\xcd\xe2\x50\ +\xf6\x90\x69\x4c\x36\x1b\xba\x04\x36\x96\x2d\x28\x99\x08\x44\x37\ +\xf7\x62\x10\x2d\x46\xbb\x09\x3a\x61\x69\x5a\xd6\xb2\x49\xcb\xa3\ +\xed\x0a\xbf\xd2\xc7\xf1\xd7\xfb\xbb\x6b\xcb\xa8\x7e\x92\xe6\x9c\ +\xf3\xbd\xf7\x9e\x73\x72\xee\x6d\xab\x20\x09\xfc\x07\xe2\xac\x0f\ +\x87\xce\xf6\xe2\x82\x79\x02\xa3\x10\xf0\xd0\xf2\x1c\x8c\xaa\x92\ +\xb1\x2c\x45\x83\xd5\xe9\x09\xa8\xce\x4a\x40\xae\xa0\xe4\xbb\xe7\ +\x13\xc7\xed\x3c\xde\xa8\xd7\x23\xb1\x7c\x07\x9c\xa2\x1f\x55\x85\ +\xb9\x58\x53\x90\x09\x8f\x42\x0d\x8d\x4a\x89\x0c\x41\x05\xd3\x0c\ +\xe1\xb8\xc5\x8b\x63\x37\x45\x69\x77\xec\xfe\x62\x76\x9e\xf0\x44\ +\x35\x20\x2c\x80\xa3\xf3\x08\x1c\x1e\x11\xf5\x3f\xfe\x81\xab\xce\ +\x38\x64\x68\xb5\xb8\x31\xab\x44\xb2\xa0\x46\x51\x7a\x22\x56\x27\ +\x29\xe1\xf5\x03\x05\x0b\x94\xa8\x5c\xaa\x92\x4e\x2a\xe4\x04\xf7\ +\x08\x25\x8f\x06\xda\x35\x74\xc7\x3e\x41\xeb\xde\x3b\x4c\xf9\x35\ +\x9f\x52\x55\xc3\x19\x32\xde\x72\xb0\xb5\xec\x53\x26\xea\xb0\x4e\ +\xd3\xab\x86\x49\x5a\xfb\x8b\x9b\xac\x1e\x3f\xd3\x2d\xee\x00\x79\ +\xfd\x41\xe6\x47\x33\x27\xf9\xd6\x0f\x1a\xe9\x58\x6b\x27\xd5\xeb\ +\xdb\x49\x51\xfc\x1a\xd5\x36\x75\xf0\x15\xa2\x62\xbd\x81\x3a\xcd\ +\x4e\xe6\x17\x9e\xb3\x33\xbb\xc5\xe0\x26\x87\x18\x60\xfe\xb0\x53\ +\x2e\x14\xcd\x9c\x99\x9f\xe9\xea\xc1\xc0\xf0\x08\xae\xfc\x3a\x88\ +\x60\xdf\x71\x7c\xdb\x7b\x1d\xfb\xdb\x2e\xe3\xa7\xa1\xdb\xc8\xd4\ +\x10\xd6\x69\x17\xb2\x7d\x63\x62\x80\xd9\xe6\xc7\x05\x1c\xb8\x21\ +\xc2\x7c\x37\x20\x69\x84\x3f\xa7\x02\xa1\x66\xd9\x5a\x88\x70\xf2\ +\xaf\x4f\x75\x23\x41\x13\x8f\xdf\x07\x4d\xe8\xfa\xaa\x8e\x69\x7f\ +\x35\xd5\xc2\x35\xed\xc5\x73\x7b\x5a\x50\x96\x9d\x02\x5f\x50\x3e\ +\x98\xa6\x96\x67\x1b\x94\x3e\x82\x46\x01\x9d\xc9\x8b\x92\x74\x15\ +\x3c\x01\xc2\x80\x53\x2e\x1c\x22\x9c\xfc\x5c\xaf\x11\x76\xf3\x6d\ +\xf4\x9c\x6e\xe0\x8a\x4c\x66\x4a\x32\x3e\xdc\x54\x84\x60\x30\x88\ +\xe2\x26\x03\x1e\x6c\x1e\x86\x6b\x26\x80\xdc\xce\x7f\x50\xd6\xeb\ +\x42\x49\x9a\x0a\x56\x5f\xa8\x28\x61\xa9\xa0\xc0\x15\xbb\x74\xc3\ +\xf7\x90\xa7\x43\x94\x5f\xfe\x26\x7d\xd4\x78\x82\x47\x11\x50\xfa\ +\x36\xf7\x22\x94\x75\x8f\x12\x05\x23\x17\xd8\x62\x15\xa9\x51\x7a\ +\x9b\x92\x48\x95\x86\xbb\xb2\x28\x11\xee\x5c\x13\xaf\xc6\xf7\x17\ +\xfb\x79\x24\xa3\xef\xb8\x8c\x1d\x55\x25\x3c\x8a\xe0\x73\x39\x31\ +\x3e\x1b\x1a\x8a\xcc\xe6\x25\xf1\x38\x6d\xf1\x48\x9e\x02\x53\xa2\ +\x57\x16\x25\xc2\xc9\xf3\x97\x67\x61\xd8\x6c\xc3\xbb\x9f\xe8\xb8\ +\x02\xec\x3e\x72\x12\x1f\xd7\xbc\xc0\xa3\x08\x33\x8e\x31\x24\x46\ +\x3d\x05\x75\x9c\x02\x23\x93\x5e\x56\x30\x2b\x2e\xc6\x85\x3e\x53\ +\xb4\x0a\x7b\xb7\x57\x63\x60\xc8\x82\xd2\x97\x77\x33\x4d\x74\xba\ +\xb1\x48\x48\x64\x7e\x34\x76\x9b\x0d\x49\xea\xb9\x5f\xfb\xd4\x80\ +\x0f\xfb\x7e\x1b\x47\x85\x74\xb1\x61\xf8\x78\x18\x48\x7d\x8a\xd9\ +\xa3\xad\x5d\x84\xb4\xa7\xa9\x7c\x4b\x2d\x8b\xef\x27\xf9\x95\x06\ +\xee\x45\x58\xf6\xf9\x25\x52\xed\x3b\xcf\x23\x99\xa8\x32\xc0\xce\ +\x5d\xaf\x63\x5b\xed\x17\xd0\xed\xdf\x8e\xb1\xf1\x29\xf4\x5f\x1b\ +\x42\x5c\xde\x7a\xe4\xad\xc8\xc6\x4b\x95\xcf\xe2\xc9\x47\xf3\xb1\ +\x42\xbb\x04\x8b\x83\x22\x1c\x2e\x0f\xfa\xfe\xbe\x85\x1f\x06\x47\ +\xd0\x76\xdd\x86\xd0\xd3\x3f\xf8\xfc\x63\x3c\x93\xcc\xbc\xdf\x96\ +\xa4\x95\x1b\xf0\x9d\x6e\x2f\x4e\xb4\x5f\xc4\xfb\xdb\x36\x62\xd5\ +\xc3\xd9\xb8\x63\x9f\xc4\xd9\x4b\xfd\x30\xde\xb4\xc2\x68\xb2\x61\ +\xc2\xe3\xc5\x23\x05\x79\xc8\xc9\xca\xc0\xd6\x8a\x22\x1c\xed\xfe\ +\x19\xe7\x07\x2c\xb8\x76\xe8\x2d\x9e\x85\xc3\xfa\xbf\x8f\xd4\xc2\ +\xcd\xa4\xcc\x5b\x4f\x17\xfa\x8c\x5c\x89\x50\xf7\x65\x2b\x9d\xec\ +\xea\xe1\x11\xd1\x1e\x5d\x3b\x65\xbe\x58\xc7\xa3\xb9\xc4\x4c\x1e\ +\x62\xd7\x67\x7a\x42\x46\x29\x1d\x6e\x6a\x27\xb7\x67\x9a\xab\xd2\ +\x81\x9c\xb5\xcc\x36\xb6\x74\x10\x56\x6e\xa4\x77\x0e\x36\xb3\x38\ +\x16\xff\xfb\x67\x11\xe2\x80\xee\x1b\xb4\x75\x5f\x85\xc5\x36\x86\ +\x59\x9f\x1f\x8b\x17\x0a\x78\x20\x2d\x05\x1b\x2a\x4a\xb0\xb3\x66\ +\x13\xdf\x15\x0b\xe0\x5f\x56\xd0\xe7\xa7\x2f\xaa\x6d\x42\x00\x00\ +\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +" + +qt_resource_name = b"\ +\x00\x07\ +\x07\x3b\xe0\xb3\ +\x00\x70\ +\x00\x6c\x00\x75\x00\x67\x00\x69\x00\x6e\x00\x73\ +\x00\x15\ +\x0d\xab\x01\x1c\ +\x00\x67\ +\x00\x65\x00\x6e\x00\x64\x00\x65\x00\x72\x00\x5f\x00\x69\x00\x6e\x00\x64\x00\x69\x00\x63\x00\x61\x00\x74\x00\x6f\x00\x72\x00\x5f\ +\x00\x74\x00\x6f\x00\x6f\x00\x6c\ +\x00\x08\ +\x0a\x61\x5a\xa7\ +\x00\x69\ +\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +" + +qt_resource_struct_v1 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ +\x00\x00\x00\x44\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +" + +qt_resource_struct_v2 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x14\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x44\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x89\x77\xc6\x6c\xc0\ +" + +qt_version = [int(v) for v in QtCore.qVersion().split('.')] +if qt_version < [5, 8, 0]: + rcc_version = 1 + qt_resource_struct = qt_resource_struct_v1 +else: + rcc_version = 2 + qt_resource_struct = qt_resource_struct_v2 + +def qInitResources(): + QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/gender_indicator_tool/resources.qrc b/gender_indicator_tool/resources.qrc new file mode 100644 index 0000000..9f15c6f --- /dev/null +++ b/gender_indicator_tool/resources.qrc @@ -0,0 +1,5 @@ + + + icon.png + +