diff --git a/UDef-ARP.py b/UDef-ARP.py
index 1e1f0bd..a582172 100644
--- a/UDef-ARP.py
+++ b/UDef-ARP.py
@@ -1,3227 +1,3227 @@
-import sys
-import os
-from PyQt5 import QtWidgets
-from PyQt5.QtCore import Qt,QUrl
-from PyQt5.QtWidgets import QDialog, QFileDialog, QMessageBox, QProgressDialog, QApplication, QWidget, QPushButton,QTextEdit,QSizePolicy
-from PyQt5.QtGui import QFontDatabase, QIcon, QFont, QDesktopServices
-from PyQt5.uic import loadUi
-from allocation_tool import AllocationTool
-from vulnerability_map import VulnerabilityMap
-from model_evaluation import ModelEvaluation
-from osgeo import gdal
-from pathlib import Path, PureWindowsPath
-import numpy as np
-import traceback
-
-# GDAL exceptions
-gdal.UseExceptions()
-
-class IntroScreen(QDialog):
- def __init__(self):
- super(IntroScreen, self).__init__()
- loadUi(Path(PureWindowsPath("data\\intro_screen.ui")), self)
- # Set window properties
- self.setWindowTitle('JNR Allocated Risk Mapping Procedure (UDef-ARP)')
- # Set window icon
- self.setWindowIcon(QIcon(str(Path(PureWindowsPath("data\\icon.ico")))))
- self.Fit_Cal_button.clicked.connect(self.gotofitcal)
- self.Pre_Cnf_button.clicked.connect(self.gotoprecnf)
- self.Fit_Hrp_button.clicked.connect(self.gotofithrp)
- self.Pre_VP_button.clicked.connect(self.gotoprevp)
- self.doc_button.clicked.connect(self.openDocument)
-
- def gotofitcal(self):
- rmt_fit_cal = RMT_FIT_CAL_SCREEN()
- widget.addWidget(rmt_fit_cal)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotoprecnf(self):
- rmt_pre_cnf = RMT_PRE_CNF_SCREEN()
- widget.addWidget(rmt_pre_cnf)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotofithrp(self):
- rmt_fit_hrp = RMT_FIT_HRP_SCREEN()
- widget.addWidget(rmt_fit_hrp)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotoprevp(self):
- rmt_pre_vp = RMT_PRE_VP_SCREEN()
- widget.addWidget(rmt_pre_vp)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\UDef-ARP_Introduction.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
-class RMT_FIT_CAL_SCREEN(QDialog):
- def __init__(self):
- super(RMT_FIT_CAL_SCREEN, self).__init__()
- # Store the initial directory path
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\rmt_fit_cal_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- if central_data_store.directory is not None and self.folder_entry_2 is not None:
- self.directory_2 = central_data_store.directory
- self.folder_entry_2.setText(str(central_data_store.directory))
- self.AT_button2.clicked.connect(self.gotoat2)
- self.Intro_button2.clicked.connect(self.gotointro2)
- self.MCT_button2.clicked.connect(self.gotomct2)
- self.doc_button = self.tab1.findChild(QWidget, "doc_button")
- self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
- self.deforestation_hrp_button = self.tab1.findChild(QWidget, "deforestation_hrp_button")
- self.mask_button = self.tab1.findChild(QWidget, "mask_button")
- self.fd_button = self.tab1.findChild(QWidget, "fd_button")
- self.calculate_button2 = self.tab1.findChild(QWidget, "calculate_button2")
- self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
-
- self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
- self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
- self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
- self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
- self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
- self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
-
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
- self.mask_button.clicked.connect(self.select_mask)
- self.fd_button.clicked.connect(self.select_fd)
- self.calculate_button2.clicked.connect(self.process_data2_nrt)
- self.ok_button2.clicked.connect(self.process_data2)
-
- self.doc_button_2.clicked.connect(self.openDocument_2)
- self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
- self.mask_button_2.clicked.connect(self.select_mask_2)
- self.fmask_button_2.clicked.connect(self.select_fmask_2)
- self.fd_button_2.clicked.connect(self.select_fd_2)
- self.ok_button2_2.clicked.connect(self.process_data2_2)
-
- self.vulnerability_map = VulnerabilityMap()
- self.vulnerability_map.progress_updated.connect(self.update_progress)
- self.directory = None
- self.in_fn = None
- self.deforestation_hrp = None
- self.mask = None
- self.NRT = None
- if central_data_store.NRT is not None:
- self.nrt_entry.setText(str(central_data_store.NRT))
- self.n_classes = None
- self.out_fn = None
- self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_CAL.tif')
- self.directory_2 = None
- self.mask_2 = None
- self.fmask_2 = None
- self.in_fn_2 = None
- self.out_fn_2 = None
- self.n_classes_2 = None
- self.file_path_directory = None
- self.file_path2_directory = None
- self.file_path3_directory = None
- self.file_path4_directory = None
- self.file_path5_directory = None
- self.file_path6_directory = None
- self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_CAL.tif')
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotoat2(self):
- os.chdir(self.initial_directory)
- at2 = AT_FIT_CAL_Screen()
- widget.addWidget(at2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotomct2(self):
- os.chdir(self.initial_directory)
- mct2 = MCT_FIT_CAL_Screen()
- widget.addWidget(mct2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro2(self):
- os.chdir(self.initial_directory)
- intro2 = IntroScreen()
- widget.addWidget(intro2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\TestFitVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def openDocument_2(self):
- pdf_path = Path(PureWindowsPath("doc\\TestFitVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- self.folder_entry_2.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_fd(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CAL')
- if file_path:
- self.in_fn = Path(PureWindowsPath(file_path))
- self.in_fn_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_deforestation_hrp(self):
- file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the CAL")
- if file_path3:
- self.deforestation_hrp = Path(PureWindowsPath(file_path3))
- self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
- self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
- def select_mask(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path2:
- self.mask = Path(PureWindowsPath(file_path2))
- self.mask_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
- def select_working_directory_2(self):
- data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path_2 = Path(data_folder_2)
- self.directory_2 = str(data_folder_path_2)
- self.folder_entry_2.setText(self.directory_2)
- self.folder_entry.setText(self.directory_2)
- central_data_store.directory = self.directory_2
-
- def select_fd_2(self):
- file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CAL')
- if file_path4:
- self.in_fn_2 = Path(PureWindowsPath(file_path4))
- self.in_fn_entry_2.setText(file_path4.split('/')[-1])
- self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
-
- def select_mask_2(self):
- file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path5:
- self.mask_2 = Path(PureWindowsPath(file_path5))
- self.mask_entry_2.setText(file_path5.split('/')[-1])
- self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
-
- def select_fmask_2(self):
- file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
- if file_path6:
- self.fmask_2 = file_path6
- self.fmask_entry_2.setText(file_path6.split('/')[-1])
- self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
-
- def get_full_path(self,base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data2_nrt(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- self.folder_entry_2.setText(directory)
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
-
- if not self.file_path_directory:
- self.file_path_directory=directory
- if not self.file_path2_directory:
- self.file_path2_directory=directory
- if not self.file_path3_directory:
- self.file_path3_directory=directory
-
- self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
- self.mask = self.get_full_path(self.file_path2_directory,self.mask_entry.text())
- self.deforestation_hrp = self.get_full_path(self.file_path3_directory,self.deforestation_hrp_entry.text())
-
- if not self.in_fn or not self.deforestation_hrp or not self.mask:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.in_fn, self.deforestation_hrp, self.mask]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- if not map_checker.check_binary_map(self.deforestation_hrp):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE HRP' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- if not map_checker.check_binary_map(self.mask):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate jurisdiction.")
- return
-
- # Show "Processing" message
- processing_message = "Calculating NRT..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Calculating")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory)
- NRT = self.vulnerability_map.nrt_calculation(self.in_fn, self.deforestation_hrp, self.mask)
- # Update the central data store
- central_data_store.NRT = NRT
- QMessageBox.information(self, "Processing Completed", f"Processing completed!\nNRT is: {NRT}")
- self.nrt_entry.setText(str(NRT))
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def process_data2(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- self.folder_entry_2.setText(directory)
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
- self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
- self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
-
- if not self.in_fn or not self.mask:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- NRT = self.nrt_entry.text()
- if not NRT:
- QMessageBox.critical(self, "Error", "Please enter the NRT value!")
- return
- try:
- self.NRT = int(NRT)
- if (self.NRT <= 0):
- QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
- return
-
- n_classes = int(29)
- if not n_classes:
- QMessageBox.critical(self, "Error", "Please enter the number of classes!")
- return
- try:
- self.n_classes = int(n_classes)
- if (self.n_classes <= 0):
- QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
- return
-
- out_fn = self.out_fn_entry.text()
- if not out_fn:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CAL!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in CAL!")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory)
- mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
- self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
-
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
-
- def process_data2_2(self):
- directory_2 = self.folder_entry_2.text()
- central_data_store.directory = directory_2
- self.folder_entry.setText(directory_2)
- if not directory_2 :
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path4_directory:
- self.file_path4_directory = directory_2
- if not self.file_path5_directory:
- self.file_path5_directory = directory_2
- if not self.file_path6_directory:
- self.file_path6_directory = directory_2
-
- self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
- self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
- self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
-
- if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.in_fn_2, self.mask_2, self.fmask_2]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- n_classes_2 = int(30)
- out_fn_2 = self.out_fn_entry_2.text()
- if not out_fn_2:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CAL!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in CAL!")
- return
-
- if not map_checker.check_binary_map(self.fmask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF FOREST AREAS IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
- return
-
- if not map_checker.check_binary_map(self.mask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory_2)
- mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2, self.mask_2, self.fmask_2)
- self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
-
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class AT_FIT_CAL_Screen(QDialog):
- def __init__(self):
- super(AT_FIT_CAL_Screen, self).__init__()
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\at_fit_cal_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- self.Intro_button3.clicked.connect(self.gotointro3)
- self.RMT_button3.clicked.connect(self.gotormt3)
- self.MCT_button3.clicked.connect(self.gotomct3)
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.municipality_button.clicked.connect(self.select_municipality)
- self.risk30_hrp_button.clicked.connect(self.select_risk30_hrp)
- self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
- self.ok_button3.clicked.connect(self.process_data3)
- self.allocation_tool = AllocationTool()
- # Connect the progress_updated signal to the update_progress method
- self.allocation_tool.progress_updated.connect(self.update_progress)
- self.directory = None
- self.risk30_hrp = None
- self.municipality = None
- self.deforestation_hrp = None
- self.out_fn1 = None
- self.out_fn2 = None
- self.csv_name = None
- self.file_path_directory = None
- self.file_path1_directory = None
- self.file_path3_directory = None
- self.image1_entry.setPlaceholderText('e.g., Acre_Modeling_Region_CAL.tif')
- self.csv_entry.setPlaceholderText('e.g., Relative_Frequency_Table_CAL.csv')
- self.image2_entry.setPlaceholderText('e.g., Acre_Fitted_Density_Map_CAL.tif')
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotormt3(self):
- os.chdir(self.initial_directory)
- rmt3 = RMT_FIT_CAL_SCREEN()
- widget.addWidget(rmt3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro3(self):
- os.chdir(self.initial_directory)
- intro3 = IntroScreen()
- widget.addWidget(intro3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotomct3(self):
- os.chdir(self.initial_directory)
- mct3 = MCT_FIT_CAL_Screen()
- widget.addWidget(mct3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\TestFitAM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_municipality(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
- if file_path:
- self.municipality = Path(PureWindowsPath(file_path))
- self.municipality_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_risk30_hrp(self):
- file_path1, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in CAL")
- if file_path1:
- self.risk30_hrp = Path(file_path1)
- self.risk30_hrp_entry.setText(file_path1.split('/')[-1])
- self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
-
- def select_deforestation_hrp(self):
- file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the CAL")
- if file_path3:
- self.deforestation_hrp = Path(file_path3)
- self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
- self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data3(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path1_directory:
- self.file_path1_directory = directory
- if not self.file_path3_directory:
- self.file_path3_directory = directory
-
- self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
- self.risk30_hrp = self.get_full_path(self.file_path1_directory, self.risk30_hrp_entry.text())
- self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
-
- if not self.risk30_hrp or not self.municipality or not self.deforestation_hrp:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.risk30_hrp, self.municipality, self.deforestation_hrp]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- out_fn1 = self.image1_entry.text()
- if not out_fn1:
- QMessageBox.critical(self, "Error", "Please enter the name for Modeling Region Map in CAL!")
- return
-
- if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name for Modeling Region Map in CAL!")
- return
-
- csv_name = self.csv_entry.text()
- if not csv_name:
- QMessageBox.critical(self, "Error", "Please enter the name for the Relative Frequency Table!")
- return
-
- if not (csv_name.endswith('.csv')):
- QMessageBox.critical(self, "Error",
- "Please enter .csv extension in the name of Relative Frequency Table!")
- return
-
- out_fn2 = self.image2_entry.text()
- if not out_fn2:
- QMessageBox.critical(self, "Error", "Please enter the name for Fitted Density Map in the CAL!")
- return
-
- if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name for Fitted Density Map in the CAL!")
- return
-
- if not map_checker.check_binary_map(self.deforestation_hrp):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.allocation_tool.execute_workflow_fit(directory, self.risk30_hrp,
- self.municipality,self.deforestation_hrp, csv_name,
- out_fn1,out_fn2)
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-
-class MCT_FIT_CAL_Screen(QDialog):
- def __init__(self):
- super(MCT_FIT_CAL_Screen, self).__init__()
- # Store the initial directory path
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\mct_fit_cal_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- self.AT_button4.clicked.connect(self.gotoat4)
- self.Intro_button4.clicked.connect(self.gotointro4)
- self.RMT_button4.clicked.connect(self.gotormt4)
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.mask_button.clicked.connect(self.select_mask)
- self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
- self.density_button.clicked.connect(self.select_density)
- self.ok_button.clicked.connect(self.process_data4)
- self.model_evaluation = ModelEvaluation()
- self.model_evaluation.progress_updated.connect(self.update_progress)
- self.directory = None
- self.mask = None
- self.deforestation_hrp = None
- self.density = None
- self.grid_area = None
- self.grid_area_entry.setPlaceholderText('Type default 100000 or other number')
- self.title = None
- self.out_fn = None
- self.out_fn_entry.setPlaceholderText('e.g., Plot_CAL.png')
- self.raster_fn = None
- self.raster_fn_entry.setPlaceholderText('e.g., Acre_Residuals_CAL.tif')
- self.xmax = None
- self.xmax = None
- self.file_path_directory = None
- self.file_path2_directory = None
- self.file_path3_directory = None
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotoat4(self):
- os.chdir(self.initial_directory)
- at4 = AT_FIT_CAL_Screen()
- widget.addWidget(at4)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro4(self):
- os.chdir(self.initial_directory)
- intro4 = IntroScreen()
- widget.addWidget(intro4)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotormt4(self):
- os.chdir(self.initial_directory)
- rmt4 = RMT_FIT_CAL_SCREEN()
- widget.addWidget(rmt4)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\TestFitMA.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_mask(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path:
- self.mask = Path(PureWindowsPath(file_path))
- self.mask_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_deforestation_hrp(self):
- file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the HRP")
- if file_path3:
- self.deforestation_hrp = Path(PureWindowsPath(file_path3))
- self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
- self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
-
- def select_density(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, 'Deforestation Density Map')
- if file_path2:
- self.density = Path(PureWindowsPath(file_path2))
- self.density_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data4(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
- if not self.file_path3_directory:
- self.file_path3_directory = directory
-
- self.mask = self.get_full_path(self.file_path_directory, self.mask_entry.text())
- self.density = self.get_full_path(self.file_path2_directory, self.density_entry.text())
- self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
-
- if not self.mask or not self.deforestation_hrp or not self.density :
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.mask, self.deforestation_hrp, self.density]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- grid_area = self.grid_area_entry.text()
- if not grid_area:
- QMessageBox.critical(self, "Error", "Please enter the thiessen polygon grid area value!")
- return
- try:
- self.grid_area = float(grid_area)
- if not (0 < self.grid_area):
- QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should be a valid number!")
- return
-
-
- xmax = self.xmax_entry.text()
- if xmax.lower() != "default":
- try:
- xmax = float(xmax)
- if xmax <= 0:
- raise ValueError("The plot x-axis limit should be larger than 0!")
- except ValueError:
- QMessageBox.critical(self, "Error", "The plot x-axis limit should be a valid number or 'Default'!")
- return
-
- ymax = self.ymax_entry.text()
- if ymax.lower() != "default":
- try:
- ymax = float(ymax)
- if ymax <= 0:
- raise ValueError("The plot y-axis limit should be larger than 0!")
- except ValueError:
- QMessageBox.critical(self, "Error", "The plot y-axis limit should be a valid number or 'Default'!")
- return
-
- title = self.title_entry.text()
- if not title:
- QMessageBox.critical(self, "Error", "Please enter the title of plot!")
- return
-
- out_fn = self.out_fn_entry.text()
- if not out_fn:
- QMessageBox.critical(self, "Error", "Please enter the name of plot!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn.endswith('.png') or out_fn.endswith('.jpg') or out_fn.endswith('.pdf') or out_fn.endswith(
- '.svg') or out_fn.endswith('.eps') or out_fn.endswith('.ps') or out_fn.endswith('.tif')):
- QMessageBox.critical(self, "Error",
- "Please enter extension(.png/.jpg/.pdf/.svg/.eps/.ps/.tif) in the name of plot!")
- return
-
- raster_fn = self.raster_fn_entry.text()
- if not raster_fn:
- QMessageBox.critical(self, "Error", "Please enter the name for Residual Map!")
- return
-
- if not (raster_fn.endswith('.tif') or raster_fn.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Residual Map!")
- return
-
- if not map_checker.check_binary_map(self.mask):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
- return
-
- if not map_checker.check_binary_map(self.deforestation_hrp):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.model_evaluation.set_working_directory(directory)
- self.model_evaluation.create_mask_polygon(self.mask)
- clipped_gdf = self.model_evaluation.create_thiessen_polygon(self.grid_area, self.mask,self.density, self.deforestation_hrp, out_fn,raster_fn)
- self.model_evaluation.replace_ref_system(self.mask, raster_fn)
- self.model_evaluation.create_plot(grid_area,clipped_gdf, title, out_fn, xmax, ymax)
- self.model_evaluation.remove_temp_files()
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class RMT_PRE_CNF_SCREEN(QDialog):
- def __init__(self):
- super(RMT_PRE_CNF_SCREEN, self).__init__()
- # Store the initial directory path
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\rmt_pre_cnf_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- if central_data_store.directory is not None and self.folder_entry_2 is not None:
- self.directory_2 = central_data_store.directory
- self.folder_entry_2.setText(str(central_data_store.directory))
- self.AT_button2.clicked.connect(self.gotoat2)
- self.Intro_button2.clicked.connect(self.gotointro2)
- self.MCT_button2.clicked.connect(self.gotomct2)
- self.doc_button = self.tab1.findChild(QWidget, "doc_button")
- self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
- self.fd_button = self.tab1.findChild(QWidget, "fd_button")
- self.mask_button = self.tab1.findChild(QWidget, "mask_button")
- self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
-
- self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
- self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
- self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
- self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
- self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
- self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
-
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.fd_button.clicked.connect(self.select_fd)
- self.mask_button.clicked.connect(self.select_mask)
- self.ok_button2.clicked.connect(self.process_data2)
-
- self.doc_button_2.clicked.connect(self.openDocument_2)
- self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
- self.mask_button_2.clicked.connect(self.select_mask_2)
- self.fmask_button_2.clicked.connect(self.select_fmask_2)
- self.fd_button_2.clicked.connect(self.select_fd_2)
- self.ok_button2_2.clicked.connect(self.process_data2_2)
-
- self.vulnerability_map = VulnerabilityMap()
- self.vulnerability_map.progress_updated.connect(self.update_progress)
- self.directory = None
- self.in_fn = None
- self.NRT = None
- self.mask = None
- # Use NRT from the data store
- if central_data_store.NRT is not None:
- self.nrt_entry.setText(str(central_data_store.NRT))
- self.n_classes = None
- self.out_fn = None
- self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_CNF.tif')
-
- self.directory_2 = None
- self.mask_2 = None
- self.fmask_2 = None
- self.in_fn_2 = None
- self.out_fn_2 = None
- self.n_classes_2 = None
- self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_CNF.tif')
- self.file_path_directory = None
- self.file_path2_directory = None
- self.file_path4_directory = None
- self.file_path5_directory = None
- self.file_path6_directory = None
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotoat2(self):
- os.chdir(self.initial_directory)
- at2 = AT_PRE_CNF_Screen()
- widget.addWidget(at2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotomct2(self):
- os.chdir(self.initial_directory)
- mct2 = MCT_PRE_CNF_Screen()
- widget.addWidget(mct2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro2(self):
- os.chdir(self.initial_directory)
- intro2 = IntroScreen()
- widget.addWidget(intro2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\TestPreVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def openDocument_2(self):
- pdf_path = Path(PureWindowsPath("doc\\TestPreVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- self.folder_entry_2.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_fd(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CNF')
- if file_path:
- self.in_fn = Path(PureWindowsPath(file_path))
- self.in_fn_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_mask(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path2:
- self.mask = Path(PureWindowsPath(file_path2))
- self.mask_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
-
- def select_working_directory_2(self):
- data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path_2 = Path(data_folder_2)
- self.directory_2 = str(data_folder_path_2)
- self.folder_entry_2.setText(self.directory_2)
- self.folder_entry.setText(self.directory_2)
- central_data_store.directory = self.directory_2
- def select_fd_2(self):
- file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CNF')
- if file_path4:
- self.in_fn_2 = Path(PureWindowsPath(file_path4))
- self.in_fn_entry_2.setText(file_path4.split('/')[-1])
- self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
-
- def select_mask_2(self):
- file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path5:
- self.mask_2 = Path(PureWindowsPath(file_path5))
- self.mask_entry_2.setText(file_path5.split('/')[-1])
- self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
-
- def select_fmask_2(self):
- file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
- if file_path6:
- self.fmask_2 = file_path6
- self.fmask_entry_2.setText(file_path6.split('/')[-1])
- self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data2(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- self.folder_entry_2.setText(directory)
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
- self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
- self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
-
- if not self.in_fn or not self.mask:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- NRT = self.nrt_entry.text()
- if not NRT:
- QMessageBox.critical(self, "Error", "Please enter the NRT value!")
- return
- try:
- self.NRT = int(NRT)
- if (self.NRT <= 0):
- QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
- return
-
- n_classes = int(29)
- if not n_classes:
- QMessageBox.critical(self, "Error", "Please enter the number of classes!")
- return
- try:
- self.n_classes = int(n_classes)
- if (self.n_classes <= 0):
- QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
- return
-
- out_fn = self.out_fn_entry.text()
- if not out_fn:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CNF!")
- return
-
- if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in CNF!")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory)
- mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
- self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def process_data2_2(self):
- directory_2 = self.folder_entry_2.text()
- central_data_store.directory = directory_2
- self.folder_entry.setText(directory_2)
- if not directory_2 :
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path4_directory:
- self.file_path4_directory = directory_2
- if not self.file_path5_directory:
- self.file_path5_directory = directory_2
- if not self.file_path6_directory:
- self.file_path6_directory = directory_2
-
- self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
- self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
- self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
-
- if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.in_fn_2, self.mask_2, self.fmask_2]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- n_classes_2 = int(30)
- out_fn_2 = self.out_fn_entry_2.text()
- if not out_fn_2:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CNF!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in CNF!")
- return
-
- if not map_checker.check_binary_map(self.fmask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF FOREST AREAS IN THE CNF' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
- return
-
- if not map_checker.check_binary_map(self.mask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory_2)
- mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2,
- self.mask_2, self.fmask_2)
- self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
-
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class AT_PRE_CNF_Screen(QDialog):
- def __init__(self):
- super(AT_PRE_CNF_Screen, self).__init__()
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\at_pre_cnf_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- self.Intro_button3.clicked.connect(self.gotointro3)
- self.RMT_button3.clicked.connect(self.gotormt3)
- self.MCT_button3.clicked.connect(self.gotomct3)
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.municipality_button.clicked.connect(self.select_municipality)
- self.csv_button.clicked.connect(self.select_csv)
- self.risk30_vp_button.clicked.connect(self.select_risk30_vp)
- self.deforestation_cnf_button.clicked.connect(self.select_deforestation_cnf)
- self.ok_button3.clicked.connect(self.process_data3)
- self.allocation_tool = AllocationTool()
- self.allocation_tool.progress_updated.connect(self.update_progress)
- self.directory = None
- self.csv = None
- self.municipality = None
- self.risk30_vp = None
- self.deforestation_cnf = None
- self.max_iterations = None
- self.image1 = None
- self.image2 = None
- self.file_path_directory = None
- self.file_path1_directory = None
- self.file_path2_directory = None
- self.file_path3_directory = None
- self.iteration_entry.setPlaceholderText('The suggestion max iteration number is 5')
- self.image1_entry.setPlaceholderText('e.g., Acre_Prediction_Modeling_Region_CNF.tif')
- self.image2_entry.setPlaceholderText('e.g., Acre_Adjucted_Density_Map_CNF.tif')
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotormt3(self):
- os.chdir(self.initial_directory)
- rmt3 = RMT_PRE_CNF_SCREEN()
- widget.addWidget(rmt3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro3(self):
- os.chdir(self.initial_directory)
- intro3 = IntroScreen()
- widget.addWidget(intro3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotomct3(self):
- os.chdir(self.initial_directory)
- mct3 = MCT_PRE_CNF_Screen()
- widget.addWidget(mct3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\TestPreAM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_municipality(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
- if file_path:
- self.municipality = Path(PureWindowsPath(file_path))
- self.municipality_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
- def select_csv(self):
- file_path1, _ = QFileDialog.getOpenFileName(self, "CAL Relative Frequency Table (.csv)")
- if file_path1:
- self.csv = Path(PureWindowsPath(file_path1))
- self.csv_entry.setText(file_path1.split('/')[-1])
- self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
-
- def select_risk30_vp(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in CNF")
- if file_path2:
- self.risk30_vp = Path(PureWindowsPath(file_path2))
- self.risk30_vp_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
-
- def select_deforestation_cnf(self):
- file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in CNF")
- if file_path3:
- self.deforestation_cnf = Path(PureWindowsPath(file_path3))
- self.deforestation_cnf_entry.setText(file_path3.split('/')[-1])
- self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
-
- def process_data3(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
-
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path1_directory:
- self.file_path1_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
- if not self.file_path3_directory:
- self.file_path3_directory = directory
-
- self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
- self.csv = self.get_full_path(self.file_path1_directory, self.csv_entry.text())
- self.risk30_vp = self.get_full_path(self.file_path2_directory, self.risk30_vp_entry.text())
- self.deforestation_cnf = self.get_full_path(self.file_path3_directory, self.deforestation_cnf_entry.text())
-
- if not self.municipality or not self.csv or not self.deforestation_cnf or not self.risk30_vp:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.municipality, self.deforestation_cnf, self.risk30_vp]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- out_fn1 = self.image1_entry.text()
- if not out_fn1:
- QMessageBox.critical(self, "Error", "Please enter the name of Prediction Modeling Region Map in CNF!")
- return
-
- if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Prediction Modeling Region Map in CNF!")
- return
-
- out_fn2 = self.image2_entry.text()
- if not out_fn2:
- QMessageBox.critical(self, "Error", "Please enter the name of Adjusted Prediction Density Map in CNF!")
- return
-
- if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Adjusted Prediction Density Map in CNF!")
- return
-
- max_iterations = self.iteration_entry.text()
- if not max_iterations:
- QMessageBox.critical(self, "Error", "Please enter the max iterations! The suggestion number is 5.")
- return
- try:
- self.max_iterations = int(max_iterations)
- except ValueError:
- QMessageBox.critical(self, "Error", "Max iteration value should be a valid number!")
- return
-
- if not map_checker.check_binary_map(self.deforestation_cnf):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE CNF' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- id_difference,iteration_count = self.allocation_tool.execute_workflow_cnf(directory,
- self.max_iterations, self.csv,
- self.municipality,
- self.deforestation_cnf,
- self.risk30_vp, out_fn1,
- out_fn2)
- if id_difference.size > 0:
- QMessageBox.warning(self, " Warning ", f"Modeling Region ID {','.join(map(str, id_difference))} do not exist in the Calculation Period. A new CSV has been created for the CAL where relative frequencies for missing bins have been estimated from corresponding vulnerability zones over the entire jurisdiction.")
- if iteration_count > int(max_iterations):
- QMessageBox.warning(self, " Warning ", f"Maximum iterations limit reached. Please increase the Maximum Iterations and try running the tool again.")
- else:
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class MCT_PRE_CNF_Screen(QDialog):
- def __init__(self):
- super(MCT_PRE_CNF_Screen, self).__init__()
- # Store the initial directory path
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\mct_pre_cnf_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- self.AT_button4.clicked.connect(self.gotoat4)
- self.Intro_button4.clicked.connect(self.gotointro4)
- self.RMT_button4.clicked.connect(self.gotormt4)
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.mask_button.clicked.connect(self.select_mask)
- self.fmask_button.clicked.connect(self.select_fmask)
- self.deforestation_cal_button.clicked.connect(self.select_deforestation_cal)
- self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
- self.density_button.clicked.connect(self.select_density)
- self.ok_button.clicked.connect(self.process_data4)
- self.model_evaluation = ModelEvaluation()
- self.model_evaluation.progress_updated.connect(self.update_progress)
- self.directory = None
- self.mask = None
- self.fmask = None
- self.deforestation_cal = None
- self.deforestation_hrp = None
- self.density = None
- self.grid_area = None
- self.grid_area_entry.setPlaceholderText('Type default 100000 or other number')
- self.title = None
- self.out_fn = None
- self.out_fn_def = None
- self.raster_fn = None
- self.file_path_directory = None
- self.file_path1_directory = None
- self.file_path2_directory = None
- self.file_path3_directory = None
- self.file_path4_directory = None
- self.out_fn_entry.setPlaceholderText('e.g., Plot_CNF.png')
- self.out_fn_def_entry.setPlaceholderText('e.g., Acre_Def_Review.tif')
- self.raster_fn_entry.setPlaceholderText('e.g., Acre_Residuals_CNF.tif')
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotoat4(self):
- os.chdir(self.initial_directory)
- at4 = AT_PRE_CNF_Screen()
- widget.addWidget(at4)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro4(self):
- os.chdir(self.initial_directory)
- intro4 = IntroScreen()
- widget.addWidget(intro4)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotormt4(self):
- os.chdir(self.initial_directory)
- rmt4 = RMT_PRE_CNF_SCREEN()
- widget.addWidget(rmt4)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\TestPreMA.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_mask(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path:
- self.mask = Path(PureWindowsPath(file_path))
- self.mask_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
- def select_fmask(self):
- file_path1, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
- if file_path1:
- self.fmask = Path(PureWindowsPath(file_path1))
- self.fmask_entry.setText(file_path1.split('/')[-1])
- self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
-
- def select_deforestation_cal(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, "Actual Deforestation Map in CAL")
- if file_path2:
- self.deforestation_cal = Path(PureWindowsPath(file_path2))
- self.deforestation_cal_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
-
- def select_deforestation_hrp(self):
- file_path3, _ = QFileDialog.getOpenFileName(self, "Actual Deforestation Map in CNF")
- if file_path3:
- self.deforestation_hrp = Path(PureWindowsPath(file_path3))
- self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
- self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
- def select_density(self):
- file_path4, _ = QFileDialog.getOpenFileName(self, 'Adjusted Prediction Density Map in CNF')
- if file_path4:
- self.density = Path(PureWindowsPath(file_path4))
- self.density_entry.setText(file_path4.split('/')[-1])
- self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
-
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
-
- def process_data4(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
-
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path1_directory:
- self.file_path1_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
- if not self.file_path3_directory:
- self.file_path3_directory = directory
- if not self.file_path4_directory:
- self.file_path4_directory = directory
-
- self.mask = self.get_full_path(self.file_path_directory, self.mask_entry.text())
- self.fmask = self.get_full_path(self.file_path1_directory, self.fmask_entry.text())
- self.deforestation_cal = self.get_full_path(self.file_path2_directory, self.deforestation_cal_entry.text())
- self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
- self.density = self.get_full_path(self.file_path4_directory, self.density_entry.text())
-
- if not self.mask or not self.fmask or not self.deforestation_cal or not self.deforestation_hrp or not self.density :
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.mask, self.fmask, self.deforestation_cal, self.deforestation_hrp, self.density]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- grid_area = self.grid_area_entry.text()
- if not grid_area:
- QMessageBox.critical(self, "Error", "Please enter the thiessen polygon grid area value!")
- return
- try:
- self.grid_area = float(grid_area)
- if not (0 < self.grid_area):
- QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should be a valid number!")
- return
-
- xmax = self.xmax_entry.text()
- if xmax.lower() != "default":
- try:
- xmax = float(xmax)
- if xmax <= 0:
- raise ValueError("The plot x-axis limit should be larger than 0!")
- except ValueError:
- QMessageBox.critical(self, "Error", "The plot x-axis limit should be a valid number or 'Default'!")
- return
-
- ymax = self.ymax_entry.text()
- if ymax.lower() != "default":
- try:
- ymax = float(ymax)
- if ymax <= 0:
- raise ValueError("The plot y-axis limit should be larger than 0!")
- except ValueError:
- QMessageBox.critical(self, "Error", "The plot y-axis limit should be a valid number or 'Default'!")
- return
-
- title = self.title_entry.text()
- if not title:
- QMessageBox.critical(self, "Error", "Please enter the title of plot!")
- return
-
- out_fn = self.out_fn_entry.text()
- if not out_fn:
- QMessageBox.critical(self, "Error", "Please enter the name used for the plot, performace chart and assessment polygons!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn.endswith('.png') or out_fn.endswith('.jpg') or out_fn.endswith('.pdf') or out_fn.endswith(
- '.svg') or out_fn.endswith('.eps') or out_fn.endswith('.ps') or out_fn.endswith('.tif')):
- QMessageBox.critical(self, "Error",
- "Please enter extension(.png/.jpg/.pdf/.svg/.eps/.ps/.tif) in the name of plot!")
- return
-
- raster_fn = self.raster_fn_entry.text()
- if not raster_fn:
- QMessageBox.critical(self, "Error", "Please enter the name for Residual Map!")
- return
-
- if not (raster_fn.endswith('.tif') or raster_fn.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Residual Map!")
- return
-
- out_fn_def = self.out_fn_def_entry.text()
- if not out_fn_def:
- QMessageBox.critical(self, "Error", "Please enter the name for Combined Deforestation Reference Map!")
- return
-
- if not (out_fn_def.endswith('.tif') or out_fn_def.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Combined Deforestation Reference Map!")
- return
-
- if not map_checker.check_binary_map(self.mask):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
- return
-
- if not map_checker.check_binary_map(self.deforestation_hrp):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE CNF' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- if not map_checker.check_binary_map(self.deforestation_cal):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- if not map_checker.check_binary_map(self.fmask):
- QMessageBox.critical(None, "Error",
- "'MASK OF FOREST AREAS IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.model_evaluation.set_working_directory(directory)
- self.model_evaluation.create_mask_polygon(self.mask)
- clipped_gdf = self.model_evaluation.create_thiessen_polygon(self.grid_area, self.mask, self.density,
- self.deforestation_hrp, out_fn, raster_fn)
- self.model_evaluation.replace_ref_system(self.mask, raster_fn)
- self.model_evaluation.create_deforestation_map(self.fmask, self.deforestation_cal, self.deforestation_hrp,
- out_fn_def)
- self.model_evaluation.replace_ref_system(self.fmask, out_fn_def)
- self.model_evaluation.replace_legend(out_fn_def)
- self.model_evaluation.create_plot(grid_area,clipped_gdf, title, out_fn, xmax, ymax)
- self.model_evaluation.remove_temp_files()
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class RMT_FIT_HRP_SCREEN(QDialog):
- def __init__(self):
- super(RMT_FIT_HRP_SCREEN, self).__init__()
- # Store the initial directory path
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\rmt_fit_hrp_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- if central_data_store.directory is not None and self.folder_entry_2 is not None:
- self.directory_2 = central_data_store.directory
- self.folder_entry_2.setText(str(central_data_store.directory))
- self.AT_button2.clicked.connect(self.gotoat2)
- self.Intro_button2.clicked.connect(self.gotointro2)
-
- self.doc_button = self.tab1.findChild(QWidget, "doc_button")
- self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
- self.fd_button = self.tab1.findChild(QWidget, "fd_button")
- self.mask_button = self.tab1.findChild(QWidget, "mask_button")
- self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
-
- self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
- self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
- self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
- self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
- self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
- self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
-
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.fd_button.clicked.connect(self.select_fd)
- self.mask_button.clicked.connect(self.select_mask)
- self.ok_button2.clicked.connect(self.process_data2)
-
- self.doc_button_2.clicked.connect(self.openDocument_2)
- self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
- self.mask_button_2.clicked.connect(self.select_mask_2)
- self.fmask_button_2.clicked.connect(self.select_fmask_2)
- self.fd_button_2.clicked.connect(self.select_fd_2)
- self.ok_button2_2.clicked.connect(self.process_data2_2)
-
- self.vulnerability_map = VulnerabilityMap()
- self.vulnerability_map.progress_updated.connect(self.update_progress)
- self.directory = None
- self.in_fn = None
- self.NRT = None
- if central_data_store.NRT is not None:
- self.nrt_entry.setText(str(central_data_store.NRT))
- self.n_classes = None
- self.mask = None
- self.out_fn = None
- self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_HRP.tif')
-
- self.directory_2 = None
- self.mask_2 = None
- self.fmask_2 = None
- self.in_fn_2 = None
- self.out_fn_2 = None
- self.n_classes_2 = None
- self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_HRP.tif')
- self.file_path_directory = None
- self.file_path2_directory = None
- self.file_path4_directory = None
- self.file_path5_directory = None
- self.file_path6_directory = None
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotoat2(self):
- os.chdir(self.initial_directory)
- at2 = AT_FIT_HRP_Screen()
- widget.addWidget(at2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro2(self):
- os.chdir(self.initial_directory)
- intro2 = IntroScreen()
- widget.addWidget(intro2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\AppFitVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def openDocument_2(self):
- pdf_path = Path(PureWindowsPath("doc\\AppFitVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- self.folder_entry_2.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_fd(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in HRP')
- if file_path:
- self.in_fn = Path(PureWindowsPath(file_path))
- self.in_fn_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_mask(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path2:
- self.mask = Path(PureWindowsPath(file_path2))
- self.mask_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
-
- def select_working_directory_2(self):
- data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path_2 = Path(data_folder_2)
- self.directory_2 = str(data_folder_path_2)
- self.folder_entry_2.setText(self.directory_2)
- self.folder_entry.setText(self.directory_2)
- central_data_store.directory = self.directory_2
-
- def select_fd_2(self):
- file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in HRP')
- if file_path4:
- self.in_fn_2 = Path(PureWindowsPath(file_path4))
- self.in_fn_entry_2.setText(file_path4.split('/')[-1])
- self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
-
- def select_mask_2(self):
- file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path5:
- self.mask_2 = Path(PureWindowsPath(file_path5))
- self.mask_entry_2.setText(file_path5.split('/')[-1])
- self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
-
- def select_fmask_2(self):
- file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
- if file_path6:
- self.fmask_2 = file_path6
- self.fmask_entry_2.setText(file_path6.split('/')[-1])
- self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data2(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- self.folder_entry_2.setText(directory)
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
- self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
- self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
-
- if not self.in_fn or not self.mask:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- NRT = self.nrt_entry.text()
- if not NRT:
- QMessageBox.critical(self, "Error", "Please enter the NRT value!")
- return
- try:
- self.NRT = int(NRT)
- if (self.NRT <= 0):
- QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
- return
-
- n_classes = int(29)
- if not n_classes:
- QMessageBox.critical(self, "Error", "Please enter the number of classes!")
- return
- try:
- self.n_classes = int(n_classes)
- if (self.n_classes <= 0):
- QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
- return
-
- out_fn = self.out_fn_entry.text()
- if not out_fn:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in HRP!")
- return
-
- if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in HRP!")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory)
- mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
- self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def process_data2_2(self):
- directory_2 = self.folder_entry_2.text()
- central_data_store.directory = directory_2
- self.folder_entry.setText(directory_2)
- if not directory_2 :
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
-
- if not self.file_path4_directory:
- self.file_path4_directory = directory_2
- if not self.file_path5_directory:
- self.file_path5_directory = directory_2
- if not self.file_path6_directory:
- self.file_path6_directory = directory_2
-
- self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
- self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
- self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
-
- if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.in_fn_2, self.mask_2, self.fmask_2]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- n_classes_2 = int(30)
- out_fn_2 = self.out_fn_entry_2.text()
- if not out_fn_2:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in HRP!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in HRP!")
- return
-
- if not map_checker.check_binary_map(self.mask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
- return
-
- if not map_checker.check_binary_map(self.fmask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF FOREST AREAS IN THE HRP' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory_2)
- mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2,
- self.mask_2, self.fmask_2)
- self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
-
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class AT_FIT_HRP_Screen(QDialog):
- def __init__(self):
- super(AT_FIT_HRP_Screen, self).__init__()
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\at_fit_hrp_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- self.Intro_button3.clicked.connect(self.gotointro3)
- self.RMT_button3.clicked.connect(self.gotormt3)
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.municipality_button.clicked.connect(self.select_municipality)
- self.risk30_hrp_button.clicked.connect(self.select_risk30_hrp)
- self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
- self.ok_button3.clicked.connect(self.process_data3)
- self.allocation_tool = AllocationTool()
- self.allocation_tool.progress_updated.connect(self.update_progress)
- self.directory = None
- self.risk30_hrp = None
- self.municipality = None
- self.deforestation_hrp = None
- self.out_fn1 = None
- self.out_fn2 = None
- self.csv_name = None
- self.file_path_directory = None
- self.file_path1_directory = None
- self.file_path3_directory = None
- self.image1_entry.setPlaceholderText('e.g., Acre_Modeling_Region_HRP.tif')
- self.csv_entry.setPlaceholderText('e.g., Relative_Frequency_Table_HRP.csv')
- self.image2_entry.setPlaceholderText('e.g., Acre_Fitted_Density_Map_HRP.tif')
-
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotormt3(self):
- os.chdir(self.initial_directory)
- rmt3 = RMT_FIT_HRP_SCREEN()
- widget.addWidget(rmt3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro3(self):
- os.chdir(self.initial_directory)
- intro3 = IntroScreen()
- widget.addWidget(intro3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\AppFitAM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_municipality(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
- if file_path:
- self.municipality = Path(PureWindowsPath(file_path))
- self.municipality_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_risk30_hrp(self):
- file_path1, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in HRP")
- if file_path1:
- self.risk30_hrp = Path(file_path1)
- self.risk30_hrp_entry.setText(file_path1.split('/')[-1])
- self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
- def select_deforestation_hrp(self):
- file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the HRP")
- if file_path3:
- self.deforestation_hrp = Path(file_path3)
- self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
- self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data3(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path1_directory:
- self.file_path1_directory = directory
- if not self.file_path3_directory:
- self.file_path3_directory = directory
-
- self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
- self.risk30_hrp = self.get_full_path(self.file_path1_directory, self.risk30_hrp_entry.text())
- self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
-
- if not self.risk30_hrp or not self.municipality or not self.deforestation_hrp:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.risk30_hrp, self.municipality, self.deforestation_hrp]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- out_fn1 = self.image1_entry.text()
- if not out_fn1:
- QMessageBox.critical(self, "Error", "Please enter the name for Modeling Region Map in HRP!")
- return
-
- if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name for Modeling Region Map in HRP!")
- return
-
- csv_name = self.csv_entry.text()
- if not csv_name:
- QMessageBox.critical(self, "Error", "Please enter the name for the Relative Frequency Table!")
- return
-
- if not (csv_name.endswith('.csv')):
- QMessageBox.critical(self, "Error",
- "Please enter .csv extension in the name of Relative Frequency Table!")
- return
-
- out_fn2 = self.image2_entry.text()
- if not out_fn2:
- QMessageBox.critical(self, "Error", "Please enter the name for Fitted Density Map in the HRP!")
- return
-
- if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Fitted Density Map in the HRP!")
- return
-
- if not map_checker.check_binary_map(self.deforestation_hrp):
- QMessageBox.critical(None, "Error",
- "'MAP OF DEFORESTATION IN THE HRP' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.allocation_tool.execute_workflow_fit(directory, self.risk30_hrp,
- self.municipality,self.deforestation_hrp, csv_name,
- out_fn1,out_fn2)
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-
-class RMT_PRE_VP_SCREEN(QDialog):
- def __init__(self):
- super(RMT_PRE_VP_SCREEN, self).__init__()
- # Store the initial directory path
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\rmt_pre_vp_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- if central_data_store.directory is not None and self.folder_entry_2 is not None:
- self.directory_2 = central_data_store.directory
- self.folder_entry_2.setText(str(central_data_store.directory))
- self.AT_button2.clicked.connect(self.gotoat2)
- self.Intro_button2.clicked.connect(self.gotointro2)
-
- self.doc_button = self.tab1.findChild(QWidget, "doc_button")
- self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
- self.fd_button = self.tab1.findChild(QWidget, "fd_button")
- self.mask_button = self.tab1.findChild(QWidget, "mask_button")
- self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
-
- self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
- self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
- self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
- self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
- self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
- self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
-
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.fd_button.clicked.connect(self.select_fd)
- self.mask_button.clicked.connect(self.select_mask)
- self.ok_button2.clicked.connect(self.process_data2)
-
- self.doc_button_2.clicked.connect(self.openDocument_2)
- self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
- self.mask_button_2.clicked.connect(self.select_mask_2)
- self.fmask_button_2.clicked.connect(self.select_fmask_2)
- self.fd_button_2.clicked.connect(self.select_fd_2)
- self.ok_button2_2.clicked.connect(self.process_data2_2)
-
- self.vulnerability_map = VulnerabilityMap()
- self.vulnerability_map.progress_updated.connect(self.update_progress)
- self.directory = None
- self.in_fn = None
- self.NRT = None
- if central_data_store.NRT is not None:
- self.nrt_entry.setText(str(central_data_store.NRT))
- self.n_classes = None
- self.mask = None
- self.out_fn = None
- self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_VP.tif')
-
- self.directory_2 = None
- self.mask_2 = None
- self.fmask_2 = None
- self.in_fn_2 = None
- self.out_fn_2 = None
- self.n_classes_2 = None
- self.file_path_directory = None
- self.file_path2_directory = None
- self.file_path4_directory = None
- self.file_path5_directory = None
- self.file_path6_directory = None
- self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_VP.tif')
-
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotoat2(self):
- os.chdir(self.initial_directory)
- at2 = AT_PRE_VP_Screen()
- widget.addWidget(at2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro2(self):
- os.chdir(self.initial_directory)
- intro2 = IntroScreen()
- widget.addWidget(intro2)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\AppPreVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def openDocument_2(self):
- pdf_path = Path(PureWindowsPath("doc\\AppPreVM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- self.folder_entry_2.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_fd(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in VP')
- if file_path:
- self.in_fn = Path(PureWindowsPath(file_path))
- self.in_fn_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_mask(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path2:
- self.mask = Path(PureWindowsPath(file_path2))
- self.mask_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
- def select_working_directory_2(self):
- data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path_2 = Path(data_folder_2)
- self.directory_2 = str(data_folder_path_2)
- self.folder_entry_2.setText(self.directory_2)
- self.folder_entry.setText(self.directory_2)
- central_data_store.directory = self.directory_2
-
- def select_fd_2(self):
- file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in VP')
- if file_path4:
- self.in_fn_2 = Path(PureWindowsPath(file_path4))
- self.in_fn_entry_2.setText(file_path4.split('/')[-1])
- self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
-
- def select_mask_2(self):
- file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
- if file_path5:
- self.mask_2 = Path(PureWindowsPath(file_path5))
- self.mask_entry_2.setText(file_path5.split('/')[-1])
- self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
-
- def select_fmask_2(self):
- file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
- if file_path6:
- self.fmask_2 = file_path6
- self.fmask_entry_2.setText(file_path6.split('/')[-1])
- self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data2(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- self.folder_entry_2.setText(directory)
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
-
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
-
- self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
- self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
-
- if not self.in_fn or not self.mask:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- NRT = self.nrt_entry.text()
- if not NRT:
- QMessageBox.critical(self, "Error", "Please enter the NRT value!")
- return
- try:
- self.NRT = int(NRT)
- if (self.NRT <= 0):
- QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
- return
-
- n_classes = int(29)
- if not n_classes:
- QMessageBox.critical(self, "Error", "Please enter the number of classes!")
- return
- try:
- self.n_classes = int(n_classes)
- if (self.n_classes <= 0):
- QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
- return
- except ValueError:
- QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
- return
-
- out_fn = self.out_fn_entry.text()
- if not out_fn:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in VP!")
- return
-
- if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in VP!")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory)
- mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
- self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def process_data2_2(self):
- directory_2 = self.folder_entry_2.text()
- central_data_store.directory = directory_2
- self.folder_entry.setText(directory_2)
- if not directory_2 :
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
-
- if not self.file_path4_directory:
- self.file_path4_directory = directory_2
- if not self.file_path5_directory:
- self.file_path5_directory = directory_2
- if not self.file_path6_directory:
- self.file_path6_directory = directory_2
-
- self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
- self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
- self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
-
- if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.in_fn_2, self.mask_2, self.fmask_2]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- n_classes_2 = int(30)
- out_fn_2 = self.out_fn_entry_2.text()
- if not out_fn_2:
- QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in VP!")
- return
-
- # Check if the out_fn has the correct file extension
- if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Vulnerability Map in VP!")
- return
-
- if not map_checker.check_binary_map(self.mask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
- return
-
- if not map_checker.check_binary_map(self.fmask_2):
- QMessageBox.critical(None, "Error",
- "'MASK OF FOREST AREAS IN THE VP' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- self.vulnerability_map.set_working_directory(directory_2)
- mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2,
- self.mask_2, self.fmask_2)
- self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
- self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
-
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
-
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class AT_PRE_VP_Screen(QDialog):
- def __init__(self):
- super(AT_PRE_VP_Screen, self).__init__()
- self.initial_directory = os.getcwd()
- loadUi(Path(PureWindowsPath("data\\at_pre_vp_screen.ui")), self)
- if central_data_store.directory is not None and self.folder_entry is not None:
- self.directory = central_data_store.directory
- self.folder_entry.setText(str(central_data_store.directory))
- self.Intro_button3.clicked.connect(self.gotointro3)
- self.RMT_button3.clicked.connect(self.gotormt3)
- self.doc_button.clicked.connect(self.openDocument)
- self.select_folder_button.clicked.connect(self.select_working_directory)
- self.municipality_button.clicked.connect(self.select_municipality)
- self.csv_button.clicked.connect(self.select_csv)
- self.risk30_vp_button.clicked.connect(self.select_risk30_vp)
- self.ok_button3.clicked.connect(self.process_data3)
- self.allocation_tool = AllocationTool()
- # Connect the progress_updated signal to the update_progress method
- self.allocation_tool.progress_updated.connect(self.update_progress)
- self.directory = None
- self.csv = None
- self.municipality = None
- self.risk30_vp = None
- self.expected_deforestation = None
- self.max_iterations = None
- self.image1 = None
- self.image2 = None
- self.file_path_directory = None
- self.file_path1_directory = None
- self.file_path2_directory = None
- self.iteration_entry.setPlaceholderText('The suggestion max iteration number is 5')
- self.image1_entry.setPlaceholderText('e.g., Acre_Prediction_Modeling_Region_VP.tif')
- self.image2_entry.setPlaceholderText('e.g., Acre_Adjucted_Density_Map_VP.tif')
- self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
-
- def gotormt3(self):
- os.chdir(self.initial_directory)
- rmt3 = RMT_PRE_VP_SCREEN()
- widget.addWidget(rmt3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def gotointro3(self):
- os.chdir(self.initial_directory)
- intro3 = IntroScreen()
- widget.addWidget(intro3)
- widget.setCurrentIndex(widget.currentIndex() + 1)
-
- def openDocument(self):
- pdf_path = Path(PureWindowsPath("doc\\AppPreAM.pdf"))
- QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
-
- def select_working_directory(self):
- data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
- data_folder_path = Path(data_folder)
- self.directory = str(data_folder_path)
- self.folder_entry.setText(self.directory)
- central_data_store.directory = self.directory
-
- def select_municipality(self):
- file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
- if file_path:
- self.municipality = Path(PureWindowsPath(file_path))
- self.municipality_entry.setText(file_path.split('/')[-1])
- self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
-
- def select_csv(self):
- file_path1, _ = QFileDialog.getOpenFileName(self, "VP Relative Frequency Table (.csv)")
- if file_path1:
- self.csv = Path(PureWindowsPath(file_path1))
- self.csv_entry.setText(file_path1.split('/')[-1])
- self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
-
- def select_risk30_vp(self):
- file_path2, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in VP")
- if file_path2:
- self.risk30_vp = Path(PureWindowsPath(file_path2))
- self.risk30_vp_entry.setText(file_path2.split('/')[-1])
- self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
- def get_full_path(self, base_dir, user_input):
- if os.path.isabs(user_input):
- return user_input
- else:
- return f"{base_dir}\\{user_input}"
- def process_data3(self):
- directory = self.folder_entry.text()
- central_data_store.directory = directory
- if not directory:
- QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
- return
- if not self.file_path_directory:
- self.file_path_directory = directory
- if not self.file_path1_directory:
- self.file_path1_directory = directory
- if not self.file_path2_directory:
- self.file_path2_directory = directory
-
- self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
- self.csv = self.get_full_path(self.file_path1_directory, self.csv_entry.text())
- self.risk30_vp = self.get_full_path(self.file_path2_directory, self.risk30_vp_entry.text())
-
- if not self.csv or not self.municipality or not self.risk30_vp:
- QMessageBox.critical(self, "Error", "Please select or enter all input files!")
- return
-
- images = [self.municipality, self.risk30_vp]
-
- # Check if all images have the same resolution
- resolutions = [map_checker.get_image_resolution(img) for img in images]
- if len(set(resolutions)) != 1:
- QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
- return
-
- # Check if all images have the same number of rows and columns
- dimensions = [map_checker.get_image_dimensions(img) for img in images]
- if len(set(dimensions)) != 1:
- QMessageBox.critical(None, "Error",
- "All the input raster images must have the same number of rows and columns!")
- return
-
- expected_deforestation = self.expected_entry.text()
- if not expected_deforestation:
- QMessageBox.critical(self, "Error", "Please enter the expected annual jurisdictional deforestation rate (ha/year)!")
- return
- try:
- self.expected_deforestation = float(expected_deforestation)
- except ValueError:
- QMessageBox.critical(self, "Error", "Expected annual jurisdictional deforestation rate should be a valid number!")
- return
-
- out_fn1 = self.image1_entry.text()
- if not out_fn1:
- QMessageBox.critical(self, "Error", "Please enter the name of Prediction Modeling Region Map in VP!")
- return
-
- if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Prediction Modeling Region Map in VP!")
- return
-
- out_fn2 = self.image2_entry.text()
- if not out_fn2:
- QMessageBox.critical(self, "Error", "Please enter the name of Adjusted Prediction Density Map in VP!")
- return
-
- if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
- QMessageBox.critical(self, "Error",
- "Please enter .rst or .tif extension in the name of Adjusted Prediction Density Map in VP!")
- return
-
- max_iterations = self.iteration_entry.text()
- if not max_iterations:
- QMessageBox.critical(self, "Error", "Please enter the max iterations! The suggestion number is 5.")
- return
- try:
- self.max_iterations = int(max_iterations)
- except ValueError:
- QMessageBox.critical(self, "Error", "Max iteration value should be a valid number!")
- return
-
- # Show "Processing" message
- processing_message = "Processing data..."
- self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
-
- # Change the font size
- font = QFont()
- font.setPointSize(9)
- self.progressDialog.setFont(font)
-
- self.progressDialog.setWindowTitle("Processing")
- self.progressDialog.setWindowModality(Qt.WindowModal)
- self.progressDialog.setMinimumDuration(0)
- self.progressDialog.resize(400, 300)
- self.progressDialog.show()
- QApplication.processEvents()
-
- try:
- id_difference,iteration_count = self.allocation_tool.execute_workflow_vp(directory, self.max_iterations,
- self.csv,
- self.municipality,
- self.expected_deforestation,
- self.risk30_vp, out_fn1,out_fn2)
-
- if id_difference.size > 0:
- QMessageBox.warning(self, " Warning ", f"Modeling Region ID {','.join(map(str, id_difference))} do not exist in the Historical Reference Period. A new CSV has been created for the HRP where relative frequencies for missing bins have been estimated from corresponding vulnerability zones over the entire jurisdiction.")
- if iteration_count > int(max_iterations):
- QMessageBox.warning(self, " Warning ",
- f"Maximum iterations limit reached. Please increase the Maximum Iterations and try running the tool again.")
- else:
- QMessageBox.information(self, "Processing Completed", "Processing completed!")
- self.progressDialog.close()
-
- except Exception:
- self.progressDialog.close()
- tb = traceback.format_exc()
-
- error_box = QMessageBox(self)
- error_box.setIcon(QMessageBox.Critical)
- error_box.setWindowTitle("Error")
- error_box.setText(f"An Error Occurred During Processing")
- error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
- error_box.setDetailedText(tb)
-
- details_frame = error_box.findChild(QTextEdit)
- if details_frame:
- details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- details_frame.setMaximumHeight(800)
- details_frame.setMaximumWidth(1000)
- details_frame.setMinimumHeight(400)
- details_frame.setMinimumWidth(600)
-
- error_box_layout = error_box.layout()
- error_box_layout.setContentsMargins(10, 10, 10, 10)
-
- copy_button = QPushButton("Copy Error Message")
- error_box.addButton(copy_button, QMessageBox.ActionRole)
- def copy_error_txt():
- QApplication.clipboard().setText(tb)
- copy_button.clicked.connect(copy_error_txt)
- error_box.exec_()
-
- def update_progress(self, value):
- # Update QProgressDialog with the new value
- if self.progressDialog is not None:
- self.progressDialog.setValue(value)
-
-class CentralDataStore:
- def __init__(self):
- self.NRT = None
- self.directory = None
-
-class MapChecker:
- def __init__(self):
- self.image=None
- self.arr=None
- self.in_fn=None
-
- def get_image_resolution(self,image):
- in_ds = gdal.Open(image)
- P = in_ds.GetGeoTransform()[1]
- return P
-
- def get_image_dimensions(self, image):
- dataset = gdal.Open(image)
- cols = dataset.RasterXSize
- rows = dataset.RasterYSize
- return rows, cols
-
- def get_image_datatype(self, image):
- in_ds = gdal.Open(image)
- in_band = in_ds.GetRasterBand(1)
- datatype = gdal.GetDataTypeName(in_band.DataType)
- return datatype
-
- def get_image_max_min(self, image):
- in_ds = gdal.Open(image)
- in_band = in_ds.GetRasterBand(1)
- min, max= in_band.ComputeRasterMinMax()
- return min, max
-
- def find_unique_values(self, arr, limit=2):
- unique_values = set()
- for value in np.nditer(arr):
- unique_values.add(value.item())
- if len(unique_values) > limit:
- return False
- return True
-
- def check_binary_map(self, in_fn):
- '''
- Check if input image is binary map
- :param in_fn: input image
- :return: True if the file is a binary map, False otherwise
- '''
- file_extension = in_fn.split('.')[-1].lower()
- file_name, _ = os.path.splitext(in_fn)
- if file_extension == 'rst':
- with open(file_name + '.rdc', 'r') as read_file:
- rdc_content = read_file.read().lower()
- byte_or_integer_binary = "data type : byte" in rdc_content or (
- "data type : integer" in rdc_content and "min. value : 0" in rdc_content and "max. value : 1" in rdc_content)
- float_binary = "data type : real" in rdc_content and "min. value : 0.0000000" in rdc_content and "max. value : 1.0000000" in rdc_content
- elif file_extension == 'tif':
- datatype = self.get_image_datatype(in_fn)
- min_val, max_val = self.get_image_max_min(in_fn)
- byte_or_integer_binary = datatype in ['Byte', 'CInt16', 'CInt32', 'Int16', 'Int32', 'UInt16',
- 'UInt32'] and max_val == 1 and min_val == 0
- float_binary = datatype in ['Float32', 'Float64', 'CFloat32', 'CFloat64'] and max_val == 1.0000000 and min_val == 0.0000000
-
- if byte_or_integer_binary or (float_binary):
- # For float_binary, use find_unique_values function to check if data only have two unique values [0.0000000, 1.0000000].
- if float_binary:
- in_ds = gdal.Open(in_fn)
- in_band = in_ds.GetRasterBand(1)
- arr = in_band.ReadAsArray()
- # If more than two unique values are found, it's not a binary map, return False.
- if not self.find_unique_values(arr, 2):
- return False
- # Binary map: byte_or_integer_binary or float_binary with two unique values [0.0000000, 1.0000000], it returns True.
- return True
- # For any other scenario, it returns False.
- return False
-
-if __name__ == "__main__":
- # main
- app = QApplication(sys.argv)
- # Load custom fonts
- font_id = QFontDatabase.addApplicationFont(str(Path(PureWindowsPath("font\\AvenirNextLTPro-DemiCn.otf"))))
-
- intro = IntroScreen()
- # Create a global instance of this store
- central_data_store = CentralDataStore()
- map_checker = MapChecker()
-
- widget = QtWidgets.QStackedWidget()
- widget.addWidget(intro)
- widget.setFixedHeight(1000)
- widget.setFixedWidth(1800)
- widget.show()
-
- try:
- sys.exit(app.exec_())
- except:
+import sys
+import os
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import Qt,QUrl
+from PyQt5.QtWidgets import QDialog, QFileDialog, QMessageBox, QProgressDialog, QApplication, QWidget, QPushButton,QTextEdit,QSizePolicy
+from PyQt5.QtGui import QFontDatabase, QIcon, QFont, QDesktopServices
+from PyQt5.uic import loadUi
+from allocation_tool import AllocationTool
+from vulnerability_map import VulnerabilityMap
+from model_evaluation import ModelEvaluation
+from osgeo import gdal
+from pathlib import Path, PureWindowsPath
+import numpy as np
+import traceback
+
+# GDAL exceptions
+gdal.UseExceptions()
+
+class IntroScreen(QDialog):
+ def __init__(self):
+ super(IntroScreen, self).__init__()
+ loadUi(Path(PureWindowsPath("data\\intro_screen.ui")), self)
+ # Set window properties
+ self.setWindowTitle('JNR Allocated Risk Mapping Procedure (UDef-ARP)')
+ # Set window icon
+ self.setWindowIcon(QIcon(str(Path(PureWindowsPath("data\\icon.ico")))))
+ self.Fit_Cal_button.clicked.connect(self.gotofitcal)
+ self.Pre_Cnf_button.clicked.connect(self.gotoprecnf)
+ self.Fit_Hrp_button.clicked.connect(self.gotofithrp)
+ self.Pre_VP_button.clicked.connect(self.gotoprevp)
+ self.doc_button.clicked.connect(self.openDocument)
+
+ def gotofitcal(self):
+ rmt_fit_cal = RMT_FIT_CAL_SCREEN()
+ widget.addWidget(rmt_fit_cal)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotoprecnf(self):
+ rmt_pre_cnf = RMT_PRE_CNF_SCREEN()
+ widget.addWidget(rmt_pre_cnf)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotofithrp(self):
+ rmt_fit_hrp = RMT_FIT_HRP_SCREEN()
+ widget.addWidget(rmt_fit_hrp)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotoprevp(self):
+ rmt_pre_vp = RMT_PRE_VP_SCREEN()
+ widget.addWidget(rmt_pre_vp)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\UDef-ARP_Introduction.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+class RMT_FIT_CAL_SCREEN(QDialog):
+ def __init__(self):
+ super(RMT_FIT_CAL_SCREEN, self).__init__()
+ # Store the initial directory path
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\rmt_fit_cal_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ if central_data_store.directory is not None and self.folder_entry_2 is not None:
+ self.directory_2 = central_data_store.directory
+ self.folder_entry_2.setText(str(central_data_store.directory))
+ self.AT_button2.clicked.connect(self.gotoat2)
+ self.Intro_button2.clicked.connect(self.gotointro2)
+ self.MCT_button2.clicked.connect(self.gotomct2)
+ self.doc_button = self.tab1.findChild(QWidget, "doc_button")
+ self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
+ self.deforestation_hrp_button = self.tab1.findChild(QWidget, "deforestation_hrp_button")
+ self.mask_button = self.tab1.findChild(QWidget, "mask_button")
+ self.fd_button = self.tab1.findChild(QWidget, "fd_button")
+ self.calculate_button2 = self.tab1.findChild(QWidget, "calculate_button2")
+ self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
+
+ self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
+ self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
+ self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
+ self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
+ self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
+ self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
+
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
+ self.mask_button.clicked.connect(self.select_mask)
+ self.fd_button.clicked.connect(self.select_fd)
+ self.calculate_button2.clicked.connect(self.process_data2_nrt)
+ self.ok_button2.clicked.connect(self.process_data2)
+
+ self.doc_button_2.clicked.connect(self.openDocument_2)
+ self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
+ self.mask_button_2.clicked.connect(self.select_mask_2)
+ self.fmask_button_2.clicked.connect(self.select_fmask_2)
+ self.fd_button_2.clicked.connect(self.select_fd_2)
+ self.ok_button2_2.clicked.connect(self.process_data2_2)
+
+ self.vulnerability_map = VulnerabilityMap()
+ self.vulnerability_map.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.in_fn = None
+ self.deforestation_hrp = None
+ self.mask = None
+ self.NRT = None
+ if central_data_store.NRT is not None:
+ self.nrt_entry.setText(str(central_data_store.NRT))
+ self.n_classes = None
+ self.out_fn = None
+ self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_CAL.tif')
+ self.directory_2 = None
+ self.mask_2 = None
+ self.fmask_2 = None
+ self.in_fn_2 = None
+ self.out_fn_2 = None
+ self.n_classes_2 = None
+ self.file_path_directory = None
+ self.file_path2_directory = None
+ self.file_path3_directory = None
+ self.file_path4_directory = None
+ self.file_path5_directory = None
+ self.file_path6_directory = None
+ self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_CAL.tif')
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotoat2(self):
+ os.chdir(self.initial_directory)
+ at2 = AT_FIT_CAL_Screen()
+ widget.addWidget(at2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotomct2(self):
+ os.chdir(self.initial_directory)
+ mct2 = MCT_FIT_CAL_Screen()
+ widget.addWidget(mct2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro2(self):
+ os.chdir(self.initial_directory)
+ intro2 = IntroScreen()
+ widget.addWidget(intro2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestFitVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def openDocument_2(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestFitVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ self.folder_entry_2.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_fd(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CAL')
+ if file_path:
+ self.in_fn = Path(PureWindowsPath(file_path))
+ self.in_fn_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_deforestation_hrp(self):
+ file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the CAL")
+ if file_path3:
+ self.deforestation_hrp = Path(PureWindowsPath(file_path3))
+ self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
+ self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
+ def select_mask(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path2:
+ self.mask = Path(PureWindowsPath(file_path2))
+ self.mask_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+ def select_working_directory_2(self):
+ data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path_2 = Path(data_folder_2)
+ self.directory_2 = str(data_folder_path_2)
+ self.folder_entry_2.setText(self.directory_2)
+ self.folder_entry.setText(self.directory_2)
+ central_data_store.directory = self.directory_2
+
+ def select_fd_2(self):
+ file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CAL')
+ if file_path4:
+ self.in_fn_2 = Path(PureWindowsPath(file_path4))
+ self.in_fn_entry_2.setText(file_path4.split('/')[-1])
+ self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
+
+ def select_mask_2(self):
+ file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path5:
+ self.mask_2 = Path(PureWindowsPath(file_path5))
+ self.mask_entry_2.setText(file_path5.split('/')[-1])
+ self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
+
+ def select_fmask_2(self):
+ file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
+ if file_path6:
+ self.fmask_2 = file_path6
+ self.fmask_entry_2.setText(file_path6.split('/')[-1])
+ self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
+
+ def get_full_path(self,base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data2_nrt(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ self.folder_entry_2.setText(directory)
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+
+ if not self.file_path_directory:
+ self.file_path_directory=directory
+ if not self.file_path2_directory:
+ self.file_path2_directory=directory
+ if not self.file_path3_directory:
+ self.file_path3_directory=directory
+
+ self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
+ self.mask = self.get_full_path(self.file_path2_directory,self.mask_entry.text())
+ self.deforestation_hrp = self.get_full_path(self.file_path3_directory,self.deforestation_hrp_entry.text())
+
+ if not self.in_fn or not self.deforestation_hrp or not self.mask:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.in_fn, self.deforestation_hrp, self.mask]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_hrp):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE HRP' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ if not map_checker.check_binary_map(self.mask):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate jurisdiction.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Calculating NRT..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Calculating")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory)
+ NRT = self.vulnerability_map.nrt_calculation(self.in_fn, self.deforestation_hrp, self.mask)
+ # Update the central data store
+ central_data_store.NRT = NRT
+ QMessageBox.information(self, "Processing Completed", f"Processing completed!\nNRT is: {NRT}")
+ self.nrt_entry.setText(str(NRT))
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def process_data2(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ self.folder_entry_2.setText(directory)
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+ self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
+ self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
+
+ if not self.in_fn or not self.mask:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ NRT = self.nrt_entry.text()
+ if not NRT:
+ QMessageBox.critical(self, "Error", "Please enter the NRT value!")
+ return
+ try:
+ self.NRT = int(NRT)
+ if (self.NRT <= 0):
+ QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
+ return
+
+ n_classes = int(29)
+ if not n_classes:
+ QMessageBox.critical(self, "Error", "Please enter the number of classes!")
+ return
+ try:
+ self.n_classes = int(n_classes)
+ if (self.n_classes <= 0):
+ QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
+ return
+
+ out_fn = self.out_fn_entry.text()
+ if not out_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CAL!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in CAL!")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory)
+ mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
+ self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+
+ def process_data2_2(self):
+ directory_2 = self.folder_entry_2.text()
+ central_data_store.directory = directory_2
+ self.folder_entry.setText(directory_2)
+ if not directory_2 :
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path4_directory:
+ self.file_path4_directory = directory_2
+ if not self.file_path5_directory:
+ self.file_path5_directory = directory_2
+ if not self.file_path6_directory:
+ self.file_path6_directory = directory_2
+
+ self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
+ self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
+ self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
+
+ if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.in_fn_2, self.mask_2, self.fmask_2]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ n_classes_2 = int(30)
+ out_fn_2 = self.out_fn_entry_2.text()
+ if not out_fn_2:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CAL!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in CAL!")
+ return
+
+ if not map_checker.check_binary_map(self.fmask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF FOREST AREAS IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
+ return
+
+ if not map_checker.check_binary_map(self.mask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory_2)
+ mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2, self.mask_2, self.fmask_2)
+ self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class AT_FIT_CAL_Screen(QDialog):
+ def __init__(self):
+ super(AT_FIT_CAL_Screen, self).__init__()
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\at_fit_cal_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ self.Intro_button3.clicked.connect(self.gotointro3)
+ self.RMT_button3.clicked.connect(self.gotormt3)
+ self.MCT_button3.clicked.connect(self.gotomct3)
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.municipality_button.clicked.connect(self.select_municipality)
+ self.risk30_hrp_button.clicked.connect(self.select_risk30_hrp)
+ self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
+ self.ok_button3.clicked.connect(self.process_data3)
+ self.allocation_tool = AllocationTool()
+ # Connect the progress_updated signal to the update_progress method
+ self.allocation_tool.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.risk30_hrp = None
+ self.municipality = None
+ self.deforestation_hrp = None
+ self.out_fn1 = None
+ self.out_fn2 = None
+ self.csv_name = None
+ self.file_path_directory = None
+ self.file_path1_directory = None
+ self.file_path3_directory = None
+ self.image1_entry.setPlaceholderText('e.g., Acre_Modeling_Region_CAL.tif')
+ self.csv_entry.setPlaceholderText('e.g., Relative_Frequency_Table_CAL.csv')
+ self.image2_entry.setPlaceholderText('e.g., Acre_Fitted_Density_Map_CAL.tif')
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotormt3(self):
+ os.chdir(self.initial_directory)
+ rmt3 = RMT_FIT_CAL_SCREEN()
+ widget.addWidget(rmt3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro3(self):
+ os.chdir(self.initial_directory)
+ intro3 = IntroScreen()
+ widget.addWidget(intro3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotomct3(self):
+ os.chdir(self.initial_directory)
+ mct3 = MCT_FIT_CAL_Screen()
+ widget.addWidget(mct3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestFitAM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_municipality(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
+ if file_path:
+ self.municipality = Path(PureWindowsPath(file_path))
+ self.municipality_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_risk30_hrp(self):
+ file_path1, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in CAL")
+ if file_path1:
+ self.risk30_hrp = Path(file_path1)
+ self.risk30_hrp_entry.setText(file_path1.split('/')[-1])
+ self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
+
+ def select_deforestation_hrp(self):
+ file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the CAL")
+ if file_path3:
+ self.deforestation_hrp = Path(file_path3)
+ self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
+ self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data3(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path1_directory:
+ self.file_path1_directory = directory
+ if not self.file_path3_directory:
+ self.file_path3_directory = directory
+
+ self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
+ self.risk30_hrp = self.get_full_path(self.file_path1_directory, self.risk30_hrp_entry.text())
+ self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
+
+ if not self.risk30_hrp or not self.municipality or not self.deforestation_hrp:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.risk30_hrp, self.municipality, self.deforestation_hrp]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ out_fn1 = self.image1_entry.text()
+ if not out_fn1:
+ QMessageBox.critical(self, "Error", "Please enter the name for Modeling Region Map in CAL!")
+ return
+
+ if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name for Modeling Region Map in CAL!")
+ return
+
+ csv_name = self.csv_entry.text()
+ if not csv_name:
+ QMessageBox.critical(self, "Error", "Please enter the name for the Relative Frequency Table!")
+ return
+
+ if not (csv_name.endswith('.csv')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .csv extension in the name of Relative Frequency Table!")
+ return
+
+ out_fn2 = self.image2_entry.text()
+ if not out_fn2:
+ QMessageBox.critical(self, "Error", "Please enter the name for Fitted Density Map in the CAL!")
+ return
+
+ if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name for Fitted Density Map in the CAL!")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_hrp):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.allocation_tool.execute_workflow_fit(directory, self.risk30_hrp,
+ self.municipality,self.deforestation_hrp, csv_name,
+ out_fn1,out_fn2)
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+
+class MCT_FIT_CAL_Screen(QDialog):
+ def __init__(self):
+ super(MCT_FIT_CAL_Screen, self).__init__()
+ # Store the initial directory path
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\mct_fit_cal_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ self.AT_button4.clicked.connect(self.gotoat4)
+ self.Intro_button4.clicked.connect(self.gotointro4)
+ self.RMT_button4.clicked.connect(self.gotormt4)
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.mask_button.clicked.connect(self.select_mask)
+ self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
+ self.density_button.clicked.connect(self.select_density)
+ self.ok_button.clicked.connect(self.process_data4)
+ self.model_evaluation = ModelEvaluation()
+ self.model_evaluation.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.mask = None
+ self.deforestation_hrp = None
+ self.density = None
+ self.grid_area = None
+ self.grid_area_entry.setPlaceholderText('Type default 100000 or other number')
+ self.title = None
+ self.out_fn = None
+ self.out_fn_entry.setPlaceholderText('e.g., Plot_CAL.png')
+ self.raster_fn = None
+ self.raster_fn_entry.setPlaceholderText('e.g., Acre_Residuals_CAL.tif')
+ self.xmax = None
+ self.xmax = None
+ self.file_path_directory = None
+ self.file_path2_directory = None
+ self.file_path3_directory = None
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotoat4(self):
+ os.chdir(self.initial_directory)
+ at4 = AT_FIT_CAL_Screen()
+ widget.addWidget(at4)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro4(self):
+ os.chdir(self.initial_directory)
+ intro4 = IntroScreen()
+ widget.addWidget(intro4)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotormt4(self):
+ os.chdir(self.initial_directory)
+ rmt4 = RMT_FIT_CAL_SCREEN()
+ widget.addWidget(rmt4)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestFitMA.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_mask(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path:
+ self.mask = Path(PureWindowsPath(file_path))
+ self.mask_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_deforestation_hrp(self):
+ file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the HRP")
+ if file_path3:
+ self.deforestation_hrp = Path(PureWindowsPath(file_path3))
+ self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
+ self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
+
+ def select_density(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, 'Deforestation Density Map')
+ if file_path2:
+ self.density = Path(PureWindowsPath(file_path2))
+ self.density_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data4(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+ if not self.file_path3_directory:
+ self.file_path3_directory = directory
+
+ self.mask = self.get_full_path(self.file_path_directory, self.mask_entry.text())
+ self.density = self.get_full_path(self.file_path2_directory, self.density_entry.text())
+ self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
+
+ if not self.mask or not self.deforestation_hrp or not self.density :
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.mask, self.deforestation_hrp, self.density]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ grid_area = self.grid_area_entry.text()
+ if not grid_area:
+ QMessageBox.critical(self, "Error", "Please enter the thiessen polygon grid area value!")
+ return
+ try:
+ self.grid_area = float(grid_area)
+ if not (0 < self.grid_area):
+ QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should be a valid number!")
+ return
+
+
+ xmax = self.xmax_entry.text()
+ if xmax.lower() != "default":
+ try:
+ xmax = float(xmax)
+ if xmax <= 0:
+ raise ValueError("The plot x-axis limit should be larger than 0!")
+ except ValueError:
+ QMessageBox.critical(self, "Error", "The plot x-axis limit should be a valid number or 'Default'!")
+ return
+
+ ymax = self.ymax_entry.text()
+ if ymax.lower() != "default":
+ try:
+ ymax = float(ymax)
+ if ymax <= 0:
+ raise ValueError("The plot y-axis limit should be larger than 0!")
+ except ValueError:
+ QMessageBox.critical(self, "Error", "The plot y-axis limit should be a valid number or 'Default'!")
+ return
+
+ title = self.title_entry.text()
+ if not title:
+ QMessageBox.critical(self, "Error", "Please enter the title of plot!")
+ return
+
+ out_fn = self.out_fn_entry.text()
+ if not out_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name of plot!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn.endswith('.png') or out_fn.endswith('.jpg') or out_fn.endswith('.pdf') or out_fn.endswith(
+ '.svg') or out_fn.endswith('.eps') or out_fn.endswith('.ps') or out_fn.endswith('.tif')):
+ QMessageBox.critical(self, "Error",
+ "Please enter extension(.png/.jpg/.pdf/.svg/.eps/.ps/.tif) in the name of plot!")
+ return
+
+ raster_fn = self.raster_fn_entry.text()
+ if not raster_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name for Residual Map!")
+ return
+
+ if not (raster_fn.endswith('.tif') or raster_fn.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Residual Map!")
+ return
+
+ if not map_checker.check_binary_map(self.mask):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_hrp):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.model_evaluation.set_working_directory(directory)
+ self.model_evaluation.create_mask_polygon(self.mask)
+ clipped_gdf = self.model_evaluation.create_thiessen_polygon(self.grid_area, self.mask,self.density, self.deforestation_hrp, out_fn,raster_fn)
+ self.model_evaluation.replace_ref_system(self.mask, raster_fn)
+ self.model_evaluation.create_plot(grid_area,clipped_gdf, title, out_fn, xmax, ymax)
+ self.model_evaluation.remove_temp_files()
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class RMT_PRE_CNF_SCREEN(QDialog):
+ def __init__(self):
+ super(RMT_PRE_CNF_SCREEN, self).__init__()
+ # Store the initial directory path
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\rmt_pre_cnf_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ if central_data_store.directory is not None and self.folder_entry_2 is not None:
+ self.directory_2 = central_data_store.directory
+ self.folder_entry_2.setText(str(central_data_store.directory))
+ self.AT_button2.clicked.connect(self.gotoat2)
+ self.Intro_button2.clicked.connect(self.gotointro2)
+ self.MCT_button2.clicked.connect(self.gotomct2)
+ self.doc_button = self.tab1.findChild(QWidget, "doc_button")
+ self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
+ self.fd_button = self.tab1.findChild(QWidget, "fd_button")
+ self.mask_button = self.tab1.findChild(QWidget, "mask_button")
+ self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
+
+ self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
+ self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
+ self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
+ self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
+ self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
+ self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
+
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.fd_button.clicked.connect(self.select_fd)
+ self.mask_button.clicked.connect(self.select_mask)
+ self.ok_button2.clicked.connect(self.process_data2)
+
+ self.doc_button_2.clicked.connect(self.openDocument_2)
+ self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
+ self.mask_button_2.clicked.connect(self.select_mask_2)
+ self.fmask_button_2.clicked.connect(self.select_fmask_2)
+ self.fd_button_2.clicked.connect(self.select_fd_2)
+ self.ok_button2_2.clicked.connect(self.process_data2_2)
+
+ self.vulnerability_map = VulnerabilityMap()
+ self.vulnerability_map.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.in_fn = None
+ self.NRT = None
+ self.mask = None
+ # Use NRT from the data store
+ if central_data_store.NRT is not None:
+ self.nrt_entry.setText(str(central_data_store.NRT))
+ self.n_classes = None
+ self.out_fn = None
+ self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_CNF.tif')
+
+ self.directory_2 = None
+ self.mask_2 = None
+ self.fmask_2 = None
+ self.in_fn_2 = None
+ self.out_fn_2 = None
+ self.n_classes_2 = None
+ self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_CNF.tif')
+ self.file_path_directory = None
+ self.file_path2_directory = None
+ self.file_path4_directory = None
+ self.file_path5_directory = None
+ self.file_path6_directory = None
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotoat2(self):
+ os.chdir(self.initial_directory)
+ at2 = AT_PRE_CNF_Screen()
+ widget.addWidget(at2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotomct2(self):
+ os.chdir(self.initial_directory)
+ mct2 = MCT_PRE_CNF_Screen()
+ widget.addWidget(mct2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro2(self):
+ os.chdir(self.initial_directory)
+ intro2 = IntroScreen()
+ widget.addWidget(intro2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestPreVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def openDocument_2(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestPreVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ self.folder_entry_2.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_fd(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CNF')
+ if file_path:
+ self.in_fn = Path(PureWindowsPath(file_path))
+ self.in_fn_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_mask(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path2:
+ self.mask = Path(PureWindowsPath(file_path2))
+ self.mask_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+
+ def select_working_directory_2(self):
+ data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path_2 = Path(data_folder_2)
+ self.directory_2 = str(data_folder_path_2)
+ self.folder_entry_2.setText(self.directory_2)
+ self.folder_entry.setText(self.directory_2)
+ central_data_store.directory = self.directory_2
+ def select_fd_2(self):
+ file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in CNF')
+ if file_path4:
+ self.in_fn_2 = Path(PureWindowsPath(file_path4))
+ self.in_fn_entry_2.setText(file_path4.split('/')[-1])
+ self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
+
+ def select_mask_2(self):
+ file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path5:
+ self.mask_2 = Path(PureWindowsPath(file_path5))
+ self.mask_entry_2.setText(file_path5.split('/')[-1])
+ self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
+
+ def select_fmask_2(self):
+ file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
+ if file_path6:
+ self.fmask_2 = file_path6
+ self.fmask_entry_2.setText(file_path6.split('/')[-1])
+ self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data2(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ self.folder_entry_2.setText(directory)
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+ self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
+ self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
+
+ if not self.in_fn or not self.mask:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ NRT = self.nrt_entry.text()
+ if not NRT:
+ QMessageBox.critical(self, "Error", "Please enter the NRT value!")
+ return
+ try:
+ self.NRT = int(NRT)
+ if (self.NRT <= 0):
+ QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
+ return
+
+ n_classes = int(29)
+ if not n_classes:
+ QMessageBox.critical(self, "Error", "Please enter the number of classes!")
+ return
+ try:
+ self.n_classes = int(n_classes)
+ if (self.n_classes <= 0):
+ QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
+ return
+
+ out_fn = self.out_fn_entry.text()
+ if not out_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CNF!")
+ return
+
+ if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in CNF!")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory)
+ mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
+ self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def process_data2_2(self):
+ directory_2 = self.folder_entry_2.text()
+ central_data_store.directory = directory_2
+ self.folder_entry.setText(directory_2)
+ if not directory_2 :
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path4_directory:
+ self.file_path4_directory = directory_2
+ if not self.file_path5_directory:
+ self.file_path5_directory = directory_2
+ if not self.file_path6_directory:
+ self.file_path6_directory = directory_2
+
+ self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
+ self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
+ self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
+
+ if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.in_fn_2, self.mask_2, self.fmask_2]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ n_classes_2 = int(30)
+ out_fn_2 = self.out_fn_entry_2.text()
+ if not out_fn_2:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in CNF!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in CNF!")
+ return
+
+ if not map_checker.check_binary_map(self.fmask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF FOREST AREAS IN THE CNF' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
+ return
+
+ if not map_checker.check_binary_map(self.mask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory_2)
+ mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2,
+ self.mask_2, self.fmask_2)
+ self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class AT_PRE_CNF_Screen(QDialog):
+ def __init__(self):
+ super(AT_PRE_CNF_Screen, self).__init__()
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\at_pre_cnf_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ self.Intro_button3.clicked.connect(self.gotointro3)
+ self.RMT_button3.clicked.connect(self.gotormt3)
+ self.MCT_button3.clicked.connect(self.gotomct3)
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.municipality_button.clicked.connect(self.select_municipality)
+ self.csv_button.clicked.connect(self.select_csv)
+ self.risk30_vp_button.clicked.connect(self.select_risk30_vp)
+ self.deforestation_cnf_button.clicked.connect(self.select_deforestation_cnf)
+ self.ok_button3.clicked.connect(self.process_data3)
+ self.allocation_tool = AllocationTool()
+ self.allocation_tool.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.csv = None
+ self.municipality = None
+ self.risk30_vp = None
+ self.deforestation_cnf = None
+ self.max_iterations = None
+ self.image1 = None
+ self.image2 = None
+ self.file_path_directory = None
+ self.file_path1_directory = None
+ self.file_path2_directory = None
+ self.file_path3_directory = None
+ self.iteration_entry.setPlaceholderText('The suggestion max iteration number is 5')
+ self.image1_entry.setPlaceholderText('e.g., Acre_Prediction_Modeling_Region_CNF.tif')
+ self.image2_entry.setPlaceholderText('e.g., Acre_Adjucted_Density_Map_CNF.tif')
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotormt3(self):
+ os.chdir(self.initial_directory)
+ rmt3 = RMT_PRE_CNF_SCREEN()
+ widget.addWidget(rmt3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro3(self):
+ os.chdir(self.initial_directory)
+ intro3 = IntroScreen()
+ widget.addWidget(intro3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotomct3(self):
+ os.chdir(self.initial_directory)
+ mct3 = MCT_PRE_CNF_Screen()
+ widget.addWidget(mct3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestPreAM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_municipality(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
+ if file_path:
+ self.municipality = Path(PureWindowsPath(file_path))
+ self.municipality_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+ def select_csv(self):
+ file_path1, _ = QFileDialog.getOpenFileName(self, "CAL Relative Frequency Table (.csv)")
+ if file_path1:
+ self.csv = Path(PureWindowsPath(file_path1))
+ self.csv_entry.setText(file_path1.split('/')[-1])
+ self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
+
+ def select_risk30_vp(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in CNF")
+ if file_path2:
+ self.risk30_vp = Path(PureWindowsPath(file_path2))
+ self.risk30_vp_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+
+ def select_deforestation_cnf(self):
+ file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in CNF")
+ if file_path3:
+ self.deforestation_cnf = Path(PureWindowsPath(file_path3))
+ self.deforestation_cnf_entry.setText(file_path3.split('/')[-1])
+ self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+
+ def process_data3(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path1_directory:
+ self.file_path1_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+ if not self.file_path3_directory:
+ self.file_path3_directory = directory
+
+ self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
+ self.csv = self.get_full_path(self.file_path1_directory, self.csv_entry.text())
+ self.risk30_vp = self.get_full_path(self.file_path2_directory, self.risk30_vp_entry.text())
+ self.deforestation_cnf = self.get_full_path(self.file_path3_directory, self.deforestation_cnf_entry.text())
+
+ if not self.municipality or not self.csv or not self.deforestation_cnf or not self.risk30_vp:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.municipality, self.deforestation_cnf, self.risk30_vp]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ out_fn1 = self.image1_entry.text()
+ if not out_fn1:
+ QMessageBox.critical(self, "Error", "Please enter the name of Prediction Modeling Region Map in CNF!")
+ return
+
+ if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Prediction Modeling Region Map in CNF!")
+ return
+
+ out_fn2 = self.image2_entry.text()
+ if not out_fn2:
+ QMessageBox.critical(self, "Error", "Please enter the name of Adjusted Prediction Density Map in CNF!")
+ return
+
+ if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Adjusted Prediction Density Map in CNF!")
+ return
+
+ max_iterations = self.iteration_entry.text()
+ if not max_iterations:
+ QMessageBox.critical(self, "Error", "Please enter the max iterations! The suggestion number is 5.")
+ return
+ try:
+ self.max_iterations = int(max_iterations)
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Max iteration value should be a valid number!")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_cnf):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE CNF' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ id_difference,iteration_count = self.allocation_tool.execute_workflow_cnf(directory,
+ self.max_iterations, self.csv,
+ self.municipality,
+ self.deforestation_cnf,
+ self.risk30_vp, out_fn1,
+ out_fn2)
+ if id_difference.size > 0:
+ QMessageBox.warning(self, " Warning ", f"Modeling Region ID {','.join(map(str, id_difference))} do not exist in the Calculation Period. A new CSV has been created for the CAL where relative frequencies for missing bins have been estimated from corresponding vulnerability zones over the entire jurisdiction.")
+ if iteration_count > int(max_iterations):
+ QMessageBox.warning(self, " Warning ", f"Maximum iterations limit reached. Please increase the Maximum Iterations and try running the tool again.")
+ else:
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class MCT_PRE_CNF_Screen(QDialog):
+ def __init__(self):
+ super(MCT_PRE_CNF_Screen, self).__init__()
+ # Store the initial directory path
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\mct_pre_cnf_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ self.AT_button4.clicked.connect(self.gotoat4)
+ self.Intro_button4.clicked.connect(self.gotointro4)
+ self.RMT_button4.clicked.connect(self.gotormt4)
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.mask_button.clicked.connect(self.select_mask)
+ self.fmask_button.clicked.connect(self.select_fmask)
+ self.deforestation_cal_button.clicked.connect(self.select_deforestation_cal)
+ self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
+ self.density_button.clicked.connect(self.select_density)
+ self.ok_button.clicked.connect(self.process_data4)
+ self.model_evaluation = ModelEvaluation()
+ self.model_evaluation.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.mask = None
+ self.fmask = None
+ self.deforestation_cal = None
+ self.deforestation_hrp = None
+ self.density = None
+ self.grid_area = None
+ self.grid_area_entry.setPlaceholderText('Type default 100000 or other number')
+ self.title = None
+ self.out_fn = None
+ self.out_fn_def = None
+ self.raster_fn = None
+ self.file_path_directory = None
+ self.file_path1_directory = None
+ self.file_path2_directory = None
+ self.file_path3_directory = None
+ self.file_path4_directory = None
+ self.out_fn_entry.setPlaceholderText('e.g., Plot_CNF.png')
+ self.out_fn_def_entry.setPlaceholderText('e.g., Acre_Def_Review.tif')
+ self.raster_fn_entry.setPlaceholderText('e.g., Acre_Residuals_CNF.tif')
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotoat4(self):
+ os.chdir(self.initial_directory)
+ at4 = AT_PRE_CNF_Screen()
+ widget.addWidget(at4)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro4(self):
+ os.chdir(self.initial_directory)
+ intro4 = IntroScreen()
+ widget.addWidget(intro4)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotormt4(self):
+ os.chdir(self.initial_directory)
+ rmt4 = RMT_PRE_CNF_SCREEN()
+ widget.addWidget(rmt4)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\TestPreMA.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_mask(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path:
+ self.mask = Path(PureWindowsPath(file_path))
+ self.mask_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+ def select_fmask(self):
+ file_path1, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
+ if file_path1:
+ self.fmask = Path(PureWindowsPath(file_path1))
+ self.fmask_entry.setText(file_path1.split('/')[-1])
+ self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
+
+ def select_deforestation_cal(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, "Actual Deforestation Map in CAL")
+ if file_path2:
+ self.deforestation_cal = Path(PureWindowsPath(file_path2))
+ self.deforestation_cal_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+
+ def select_deforestation_hrp(self):
+ file_path3, _ = QFileDialog.getOpenFileName(self, "Actual Deforestation Map in CNF")
+ if file_path3:
+ self.deforestation_hrp = Path(PureWindowsPath(file_path3))
+ self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
+ self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
+ def select_density(self):
+ file_path4, _ = QFileDialog.getOpenFileName(self, 'Adjusted Prediction Density Map in CNF')
+ if file_path4:
+ self.density = Path(PureWindowsPath(file_path4))
+ self.density_entry.setText(file_path4.split('/')[-1])
+ self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
+
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+
+ def process_data4(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path1_directory:
+ self.file_path1_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+ if not self.file_path3_directory:
+ self.file_path3_directory = directory
+ if not self.file_path4_directory:
+ self.file_path4_directory = directory
+
+ self.mask = self.get_full_path(self.file_path_directory, self.mask_entry.text())
+ self.fmask = self.get_full_path(self.file_path1_directory, self.fmask_entry.text())
+ self.deforestation_cal = self.get_full_path(self.file_path2_directory, self.deforestation_cal_entry.text())
+ self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
+ self.density = self.get_full_path(self.file_path4_directory, self.density_entry.text())
+
+ if not self.mask or not self.fmask or not self.deforestation_cal or not self.deforestation_hrp or not self.density :
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.mask, self.fmask, self.deforestation_cal, self.deforestation_hrp, self.density]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ grid_area = self.grid_area_entry.text()
+ if not grid_area:
+ QMessageBox.critical(self, "Error", "Please enter the thiessen polygon grid area value!")
+ return
+ try:
+ self.grid_area = float(grid_area)
+ if not (0 < self.grid_area):
+ QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Thiessen polygon grid area value should be a valid number!")
+ return
+
+ xmax = self.xmax_entry.text()
+ if xmax.lower() != "default":
+ try:
+ xmax = float(xmax)
+ if xmax <= 0:
+ raise ValueError("The plot x-axis limit should be larger than 0!")
+ except ValueError:
+ QMessageBox.critical(self, "Error", "The plot x-axis limit should be a valid number or 'Default'!")
+ return
+
+ ymax = self.ymax_entry.text()
+ if ymax.lower() != "default":
+ try:
+ ymax = float(ymax)
+ if ymax <= 0:
+ raise ValueError("The plot y-axis limit should be larger than 0!")
+ except ValueError:
+ QMessageBox.critical(self, "Error", "The plot y-axis limit should be a valid number or 'Default'!")
+ return
+
+ title = self.title_entry.text()
+ if not title:
+ QMessageBox.critical(self, "Error", "Please enter the title of plot!")
+ return
+
+ out_fn = self.out_fn_entry.text()
+ if not out_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name used for the plot, performace chart and assessment polygons!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn.endswith('.png') or out_fn.endswith('.jpg') or out_fn.endswith('.pdf') or out_fn.endswith(
+ '.svg') or out_fn.endswith('.eps') or out_fn.endswith('.ps') or out_fn.endswith('.tif')):
+ QMessageBox.critical(self, "Error",
+ "Please enter extension(.png/.jpg/.pdf/.svg/.eps/.ps/.tif) in the name of plot!")
+ return
+
+ raster_fn = self.raster_fn_entry.text()
+ if not raster_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name for Residual Map!")
+ return
+
+ if not (raster_fn.endswith('.tif') or raster_fn.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Residual Map!")
+ return
+
+ out_fn_def = self.out_fn_def_entry.text()
+ if not out_fn_def:
+ QMessageBox.critical(self, "Error", "Please enter the name for Combined Deforestation Reference Map!")
+ return
+
+ if not (out_fn_def.endswith('.tif') or out_fn_def.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Combined Deforestation Reference Map!")
+ return
+
+ if not map_checker.check_binary_map(self.mask):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_hrp):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE CNF' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_cal):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ if not map_checker.check_binary_map(self.fmask):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF FOREST AREAS IN THE CAL' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.model_evaluation.set_working_directory(directory)
+ self.model_evaluation.create_mask_polygon(self.mask)
+ clipped_gdf = self.model_evaluation.create_thiessen_polygon(self.grid_area, self.mask, self.density,
+ self.deforestation_hrp, out_fn, raster_fn)
+ self.model_evaluation.replace_ref_system(self.mask, raster_fn)
+ self.model_evaluation.create_deforestation_map(self.mask, self.fmask, self.deforestation_cal, self.deforestation_hrp,
+ out_fn_def)
+ self.model_evaluation.replace_ref_system(self.fmask, out_fn_def)
+ self.model_evaluation.replace_legend(out_fn_def)
+ self.model_evaluation.create_plot(grid_area,clipped_gdf, title, out_fn, xmax, ymax)
+ self.model_evaluation.remove_temp_files()
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class RMT_FIT_HRP_SCREEN(QDialog):
+ def __init__(self):
+ super(RMT_FIT_HRP_SCREEN, self).__init__()
+ # Store the initial directory path
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\rmt_fit_hrp_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ if central_data_store.directory is not None and self.folder_entry_2 is not None:
+ self.directory_2 = central_data_store.directory
+ self.folder_entry_2.setText(str(central_data_store.directory))
+ self.AT_button2.clicked.connect(self.gotoat2)
+ self.Intro_button2.clicked.connect(self.gotointro2)
+
+ self.doc_button = self.tab1.findChild(QWidget, "doc_button")
+ self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
+ self.fd_button = self.tab1.findChild(QWidget, "fd_button")
+ self.mask_button = self.tab1.findChild(QWidget, "mask_button")
+ self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
+
+ self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
+ self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
+ self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
+ self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
+ self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
+ self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
+
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.fd_button.clicked.connect(self.select_fd)
+ self.mask_button.clicked.connect(self.select_mask)
+ self.ok_button2.clicked.connect(self.process_data2)
+
+ self.doc_button_2.clicked.connect(self.openDocument_2)
+ self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
+ self.mask_button_2.clicked.connect(self.select_mask_2)
+ self.fmask_button_2.clicked.connect(self.select_fmask_2)
+ self.fd_button_2.clicked.connect(self.select_fd_2)
+ self.ok_button2_2.clicked.connect(self.process_data2_2)
+
+ self.vulnerability_map = VulnerabilityMap()
+ self.vulnerability_map.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.in_fn = None
+ self.NRT = None
+ if central_data_store.NRT is not None:
+ self.nrt_entry.setText(str(central_data_store.NRT))
+ self.n_classes = None
+ self.mask = None
+ self.out_fn = None
+ self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_HRP.tif')
+
+ self.directory_2 = None
+ self.mask_2 = None
+ self.fmask_2 = None
+ self.in_fn_2 = None
+ self.out_fn_2 = None
+ self.n_classes_2 = None
+ self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_HRP.tif')
+ self.file_path_directory = None
+ self.file_path2_directory = None
+ self.file_path4_directory = None
+ self.file_path5_directory = None
+ self.file_path6_directory = None
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotoat2(self):
+ os.chdir(self.initial_directory)
+ at2 = AT_FIT_HRP_Screen()
+ widget.addWidget(at2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro2(self):
+ os.chdir(self.initial_directory)
+ intro2 = IntroScreen()
+ widget.addWidget(intro2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\AppFitVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def openDocument_2(self):
+ pdf_path = Path(PureWindowsPath("doc\\AppFitVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ self.folder_entry_2.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_fd(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in HRP')
+ if file_path:
+ self.in_fn = Path(PureWindowsPath(file_path))
+ self.in_fn_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_mask(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path2:
+ self.mask = Path(PureWindowsPath(file_path2))
+ self.mask_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+
+ def select_working_directory_2(self):
+ data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path_2 = Path(data_folder_2)
+ self.directory_2 = str(data_folder_path_2)
+ self.folder_entry_2.setText(self.directory_2)
+ self.folder_entry.setText(self.directory_2)
+ central_data_store.directory = self.directory_2
+
+ def select_fd_2(self):
+ file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in HRP')
+ if file_path4:
+ self.in_fn_2 = Path(PureWindowsPath(file_path4))
+ self.in_fn_entry_2.setText(file_path4.split('/')[-1])
+ self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
+
+ def select_mask_2(self):
+ file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path5:
+ self.mask_2 = Path(PureWindowsPath(file_path5))
+ self.mask_entry_2.setText(file_path5.split('/')[-1])
+ self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
+
+ def select_fmask_2(self):
+ file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
+ if file_path6:
+ self.fmask_2 = file_path6
+ self.fmask_entry_2.setText(file_path6.split('/')[-1])
+ self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data2(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ self.folder_entry_2.setText(directory)
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+ self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
+ self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
+
+ if not self.in_fn or not self.mask:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ NRT = self.nrt_entry.text()
+ if not NRT:
+ QMessageBox.critical(self, "Error", "Please enter the NRT value!")
+ return
+ try:
+ self.NRT = int(NRT)
+ if (self.NRT <= 0):
+ QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
+ return
+
+ n_classes = int(29)
+ if not n_classes:
+ QMessageBox.critical(self, "Error", "Please enter the number of classes!")
+ return
+ try:
+ self.n_classes = int(n_classes)
+ if (self.n_classes <= 0):
+ QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
+ return
+
+ out_fn = self.out_fn_entry.text()
+ if not out_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in HRP!")
+ return
+
+ if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in HRP!")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory)
+ mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
+ self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def process_data2_2(self):
+ directory_2 = self.folder_entry_2.text()
+ central_data_store.directory = directory_2
+ self.folder_entry.setText(directory_2)
+ if not directory_2 :
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+
+ if not self.file_path4_directory:
+ self.file_path4_directory = directory_2
+ if not self.file_path5_directory:
+ self.file_path5_directory = directory_2
+ if not self.file_path6_directory:
+ self.file_path6_directory = directory_2
+
+ self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
+ self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
+ self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
+
+ if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.in_fn_2, self.mask_2, self.fmask_2]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ n_classes_2 = int(30)
+ out_fn_2 = self.out_fn_entry_2.text()
+ if not out_fn_2:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in HRP!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in HRP!")
+ return
+
+ if not map_checker.check_binary_map(self.mask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
+ return
+
+ if not map_checker.check_binary_map(self.fmask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF FOREST AREAS IN THE HRP' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory_2)
+ mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2,
+ self.mask_2, self.fmask_2)
+ self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class AT_FIT_HRP_Screen(QDialog):
+ def __init__(self):
+ super(AT_FIT_HRP_Screen, self).__init__()
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\at_fit_hrp_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ self.Intro_button3.clicked.connect(self.gotointro3)
+ self.RMT_button3.clicked.connect(self.gotormt3)
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.municipality_button.clicked.connect(self.select_municipality)
+ self.risk30_hrp_button.clicked.connect(self.select_risk30_hrp)
+ self.deforestation_hrp_button.clicked.connect(self.select_deforestation_hrp)
+ self.ok_button3.clicked.connect(self.process_data3)
+ self.allocation_tool = AllocationTool()
+ self.allocation_tool.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.risk30_hrp = None
+ self.municipality = None
+ self.deforestation_hrp = None
+ self.out_fn1 = None
+ self.out_fn2 = None
+ self.csv_name = None
+ self.file_path_directory = None
+ self.file_path1_directory = None
+ self.file_path3_directory = None
+ self.image1_entry.setPlaceholderText('e.g., Acre_Modeling_Region_HRP.tif')
+ self.csv_entry.setPlaceholderText('e.g., Relative_Frequency_Table_HRP.csv')
+ self.image2_entry.setPlaceholderText('e.g., Acre_Fitted_Density_Map_HRP.tif')
+
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotormt3(self):
+ os.chdir(self.initial_directory)
+ rmt3 = RMT_FIT_HRP_SCREEN()
+ widget.addWidget(rmt3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro3(self):
+ os.chdir(self.initial_directory)
+ intro3 = IntroScreen()
+ widget.addWidget(intro3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\AppFitAM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_municipality(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
+ if file_path:
+ self.municipality = Path(PureWindowsPath(file_path))
+ self.municipality_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_risk30_hrp(self):
+ file_path1, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in HRP")
+ if file_path1:
+ self.risk30_hrp = Path(file_path1)
+ self.risk30_hrp_entry.setText(file_path1.split('/')[-1])
+ self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
+ def select_deforestation_hrp(self):
+ file_path3, _ = QFileDialog.getOpenFileName(self, "Map of Deforestation in the HRP")
+ if file_path3:
+ self.deforestation_hrp = Path(file_path3)
+ self.deforestation_hrp_entry.setText(file_path3.split('/')[-1])
+ self.file_path3_directory = '\\'.join(file_path3.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data3(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path1_directory:
+ self.file_path1_directory = directory
+ if not self.file_path3_directory:
+ self.file_path3_directory = directory
+
+ self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
+ self.risk30_hrp = self.get_full_path(self.file_path1_directory, self.risk30_hrp_entry.text())
+ self.deforestation_hrp = self.get_full_path(self.file_path3_directory, self.deforestation_hrp_entry.text())
+
+ if not self.risk30_hrp or not self.municipality or not self.deforestation_hrp:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.risk30_hrp, self.municipality, self.deforestation_hrp]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ out_fn1 = self.image1_entry.text()
+ if not out_fn1:
+ QMessageBox.critical(self, "Error", "Please enter the name for Modeling Region Map in HRP!")
+ return
+
+ if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name for Modeling Region Map in HRP!")
+ return
+
+ csv_name = self.csv_entry.text()
+ if not csv_name:
+ QMessageBox.critical(self, "Error", "Please enter the name for the Relative Frequency Table!")
+ return
+
+ if not (csv_name.endswith('.csv')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .csv extension in the name of Relative Frequency Table!")
+ return
+
+ out_fn2 = self.image2_entry.text()
+ if not out_fn2:
+ QMessageBox.critical(self, "Error", "Please enter the name for Fitted Density Map in the HRP!")
+ return
+
+ if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Fitted Density Map in the HRP!")
+ return
+
+ if not map_checker.check_binary_map(self.deforestation_hrp):
+ QMessageBox.critical(None, "Error",
+ "'MAP OF DEFORESTATION IN THE HRP' must be a binary map (0 and 1) where the 1’s indicate deforestation.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.allocation_tool.execute_workflow_fit(directory, self.risk30_hrp,
+ self.municipality,self.deforestation_hrp, csv_name,
+ out_fn1,out_fn2)
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+
+class RMT_PRE_VP_SCREEN(QDialog):
+ def __init__(self):
+ super(RMT_PRE_VP_SCREEN, self).__init__()
+ # Store the initial directory path
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\rmt_pre_vp_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ if central_data_store.directory is not None and self.folder_entry_2 is not None:
+ self.directory_2 = central_data_store.directory
+ self.folder_entry_2.setText(str(central_data_store.directory))
+ self.AT_button2.clicked.connect(self.gotoat2)
+ self.Intro_button2.clicked.connect(self.gotointro2)
+
+ self.doc_button = self.tab1.findChild(QWidget, "doc_button")
+ self.select_folder_button = self.tab1.findChild(QWidget, "select_folder_button")
+ self.fd_button = self.tab1.findChild(QWidget, "fd_button")
+ self.mask_button = self.tab1.findChild(QWidget, "mask_button")
+ self.ok_button2 = self.tab1.findChild(QWidget, "ok_button2")
+
+ self.doc_button_2 = self.tab2.findChild(QWidget, "doc_button_2")
+ self.select_folder_button_2 = self.tab2.findChild(QWidget, "select_folder_button_2")
+ self.mask_button_2 = self.tab2.findChild(QWidget, "mask_button_2")
+ self.fmask_button_2 = self.tab2.findChild(QWidget, "fmask_button_2")
+ self.fd_button_2 = self.tab2.findChild(QWidget, "fd_button_2")
+ self.ok_button2_2 = self.tab2.findChild(QWidget, "ok_button2_2")
+
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.fd_button.clicked.connect(self.select_fd)
+ self.mask_button.clicked.connect(self.select_mask)
+ self.ok_button2.clicked.connect(self.process_data2)
+
+ self.doc_button_2.clicked.connect(self.openDocument_2)
+ self.select_folder_button_2.clicked.connect(self.select_working_directory_2)
+ self.mask_button_2.clicked.connect(self.select_mask_2)
+ self.fmask_button_2.clicked.connect(self.select_fmask_2)
+ self.fd_button_2.clicked.connect(self.select_fd_2)
+ self.ok_button2_2.clicked.connect(self.process_data2_2)
+
+ self.vulnerability_map = VulnerabilityMap()
+ self.vulnerability_map.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.in_fn = None
+ self.NRT = None
+ if central_data_store.NRT is not None:
+ self.nrt_entry.setText(str(central_data_store.NRT))
+ self.n_classes = None
+ self.mask = None
+ self.out_fn = None
+ self.out_fn_entry.setPlaceholderText('e.g., Acre_Vulnerability_VP.tif')
+
+ self.directory_2 = None
+ self.mask_2 = None
+ self.fmask_2 = None
+ self.in_fn_2 = None
+ self.out_fn_2 = None
+ self.n_classes_2 = None
+ self.file_path_directory = None
+ self.file_path2_directory = None
+ self.file_path4_directory = None
+ self.file_path5_directory = None
+ self.file_path6_directory = None
+ self.out_fn_entry_2.setPlaceholderText('e.g., Acre_Vulnerability_VP.tif')
+
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotoat2(self):
+ os.chdir(self.initial_directory)
+ at2 = AT_PRE_VP_Screen()
+ widget.addWidget(at2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro2(self):
+ os.chdir(self.initial_directory)
+ intro2 = IntroScreen()
+ widget.addWidget(intro2)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\AppPreVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def openDocument_2(self):
+ pdf_path = Path(PureWindowsPath("doc\\AppPreVM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ self.folder_entry_2.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_fd(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in VP')
+ if file_path:
+ self.in_fn = Path(PureWindowsPath(file_path))
+ self.in_fn_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_mask(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path2:
+ self.mask = Path(PureWindowsPath(file_path2))
+ self.mask_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+ def select_working_directory_2(self):
+ data_folder_2 = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path_2 = Path(data_folder_2)
+ self.directory_2 = str(data_folder_path_2)
+ self.folder_entry_2.setText(self.directory_2)
+ self.folder_entry.setText(self.directory_2)
+ central_data_store.directory = self.directory_2
+
+ def select_fd_2(self):
+ file_path4, _ = QFileDialog.getOpenFileName(self, 'Map of Distance from the Forest Edge in VP')
+ if file_path4:
+ self.in_fn_2 = Path(PureWindowsPath(file_path4))
+ self.in_fn_entry_2.setText(file_path4.split('/')[-1])
+ self.file_path4_directory = '\\'.join(file_path4.split('/')[:-1])
+
+ def select_mask_2(self):
+ file_path5, _ = QFileDialog.getOpenFileName(self, 'Mask of Study Area')
+ if file_path5:
+ self.mask_2 = Path(PureWindowsPath(file_path5))
+ self.mask_entry_2.setText(file_path5.split('/')[-1])
+ self.file_path5_directory = '\\'.join(file_path5.split('/')[:-1])
+
+ def select_fmask_2(self):
+ file_path6, _ = QFileDialog.getOpenFileName(self, 'Mask of Forest Area')
+ if file_path6:
+ self.fmask_2 = file_path6
+ self.fmask_entry_2.setText(file_path6.split('/')[-1])
+ self.file_path6_directory = '\\'.join(file_path6.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data2(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ self.folder_entry_2.setText(directory)
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+
+ self.in_fn = self.get_full_path(self.file_path_directory, self.in_fn_entry.text())
+ self.mask = self.get_full_path(self.file_path2_directory, self.mask_entry.text())
+
+ if not self.in_fn or not self.mask:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ NRT = self.nrt_entry.text()
+ if not NRT:
+ QMessageBox.critical(self, "Error", "Please enter the NRT value!")
+ return
+ try:
+ self.NRT = int(NRT)
+ if (self.NRT <= 0):
+ QMessageBox.critical(self, "Error", "NRT value should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "NRT value should be a valid number!")
+ return
+
+ n_classes = int(29)
+ if not n_classes:
+ QMessageBox.critical(self, "Error", "Please enter the number of classes!")
+ return
+ try:
+ self.n_classes = int(n_classes)
+ if (self.n_classes <= 0):
+ QMessageBox.critical(self, "Error", "Number of classes should be larger than 0!")
+ return
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Number of classes value should be a valid number!")
+ return
+
+ out_fn = self.out_fn_entry.text()
+ if not out_fn:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in VP!")
+ return
+
+ if not (out_fn.endswith('.tif') or out_fn.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in VP!")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory)
+ mask_arr = self.vulnerability_map.geometric_classification(self.in_fn, NRT, n_classes, self.mask)
+ self.vulnerability_map.array_to_image(self.in_fn, out_fn, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn, out_fn)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def process_data2_2(self):
+ directory_2 = self.folder_entry_2.text()
+ central_data_store.directory = directory_2
+ self.folder_entry.setText(directory_2)
+ if not directory_2 :
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+
+ if not self.file_path4_directory:
+ self.file_path4_directory = directory_2
+ if not self.file_path5_directory:
+ self.file_path5_directory = directory_2
+ if not self.file_path6_directory:
+ self.file_path6_directory = directory_2
+
+ self.in_fn_2 = self.get_full_path(self.file_path4_directory, self.in_fn_entry_2.text())
+ self.mask_2 = self.get_full_path(self.file_path5_directory, self.mask_entry_2.text())
+ self.fmask_2 = self.get_full_path(self.file_path6_directory, self.fmask_entry_2.text())
+
+ if not self.in_fn_2 or not self.mask_2 or not self.fmask_2:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.in_fn_2, self.mask_2, self.fmask_2]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ n_classes_2 = int(30)
+ out_fn_2 = self.out_fn_entry_2.text()
+ if not out_fn_2:
+ QMessageBox.critical(self, "Error", "Please enter the name of Vulnerability Map in VP!")
+ return
+
+ # Check if the out_fn has the correct file extension
+ if not (out_fn_2.endswith('.tif') or out_fn_2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Vulnerability Map in VP!")
+ return
+
+ if not map_checker.check_binary_map(self.mask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF THE NON-EXCLUDED JURISDICTION' must be a binary map (0 and 1) where the 1’s indicate areas inside the jurisdiction.")
+ return
+
+ if not map_checker.check_binary_map(self.fmask_2):
+ QMessageBox.critical(None, "Error",
+ "'MASK OF FOREST AREAS IN THE VP' must be a binary map (0 and 1) where the 1’s indicate forest areas.")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ self.vulnerability_map.set_working_directory(directory_2)
+ mask_arr = self.vulnerability_map.geometric_classification_alternative(self.in_fn_2, n_classes_2,
+ self.mask_2, self.fmask_2)
+ self.vulnerability_map.array_to_image(self.in_fn_2, out_fn_2, mask_arr, gdal.GDT_Int16, -1)
+ self.vulnerability_map.replace_ref_system(self.in_fn_2, out_fn_2)
+
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class AT_PRE_VP_Screen(QDialog):
+ def __init__(self):
+ super(AT_PRE_VP_Screen, self).__init__()
+ self.initial_directory = os.getcwd()
+ loadUi(Path(PureWindowsPath("data\\at_pre_vp_screen.ui")), self)
+ if central_data_store.directory is not None and self.folder_entry is not None:
+ self.directory = central_data_store.directory
+ self.folder_entry.setText(str(central_data_store.directory))
+ self.Intro_button3.clicked.connect(self.gotointro3)
+ self.RMT_button3.clicked.connect(self.gotormt3)
+ self.doc_button.clicked.connect(self.openDocument)
+ self.select_folder_button.clicked.connect(self.select_working_directory)
+ self.municipality_button.clicked.connect(self.select_municipality)
+ self.csv_button.clicked.connect(self.select_csv)
+ self.risk30_vp_button.clicked.connect(self.select_risk30_vp)
+ self.ok_button3.clicked.connect(self.process_data3)
+ self.allocation_tool = AllocationTool()
+ # Connect the progress_updated signal to the update_progress method
+ self.allocation_tool.progress_updated.connect(self.update_progress)
+ self.directory = None
+ self.csv = None
+ self.municipality = None
+ self.risk30_vp = None
+ self.expected_deforestation = None
+ self.max_iterations = None
+ self.image1 = None
+ self.image2 = None
+ self.file_path_directory = None
+ self.file_path1_directory = None
+ self.file_path2_directory = None
+ self.iteration_entry.setPlaceholderText('The suggestion max iteration number is 5')
+ self.image1_entry.setPlaceholderText('e.g., Acre_Prediction_Modeling_Region_VP.tif')
+ self.image2_entry.setPlaceholderText('e.g., Acre_Adjucted_Density_Map_VP.tif')
+ self.setWindowTitle("JNR Integrated Risk/Allocation Tool")
+
+ def gotormt3(self):
+ os.chdir(self.initial_directory)
+ rmt3 = RMT_PRE_VP_SCREEN()
+ widget.addWidget(rmt3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def gotointro3(self):
+ os.chdir(self.initial_directory)
+ intro3 = IntroScreen()
+ widget.addWidget(intro3)
+ widget.setCurrentIndex(widget.currentIndex() + 1)
+
+ def openDocument(self):
+ pdf_path = Path(PureWindowsPath("doc\\AppPreAM.pdf"))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(str(pdf_path)))
+
+ def select_working_directory(self):
+ data_folder = QFileDialog.getExistingDirectory(self, "Working Directory")
+ data_folder_path = Path(data_folder)
+ self.directory = str(data_folder_path)
+ self.folder_entry.setText(self.directory)
+ central_data_store.directory = self.directory
+
+ def select_municipality(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, 'Map of Administrative Divisions')
+ if file_path:
+ self.municipality = Path(PureWindowsPath(file_path))
+ self.municipality_entry.setText(file_path.split('/')[-1])
+ self.file_path_directory = '\\'.join(file_path.split('/')[:-1])
+
+ def select_csv(self):
+ file_path1, _ = QFileDialog.getOpenFileName(self, "VP Relative Frequency Table (.csv)")
+ if file_path1:
+ self.csv = Path(PureWindowsPath(file_path1))
+ self.csv_entry.setText(file_path1.split('/')[-1])
+ self.file_path1_directory = '\\'.join(file_path1.split('/')[:-1])
+
+ def select_risk30_vp(self):
+ file_path2, _ = QFileDialog.getOpenFileName(self, "Vulnerability Map in VP")
+ if file_path2:
+ self.risk30_vp = Path(PureWindowsPath(file_path2))
+ self.risk30_vp_entry.setText(file_path2.split('/')[-1])
+ self.file_path2_directory = '\\'.join(file_path2.split('/')[:-1])
+ def get_full_path(self, base_dir, user_input):
+ if os.path.isabs(user_input):
+ return user_input
+ else:
+ return f"{base_dir}\\{user_input}"
+ def process_data3(self):
+ directory = self.folder_entry.text()
+ central_data_store.directory = directory
+ if not directory:
+ QMessageBox.critical(self, "Error", "Please select or enter the working directory!")
+ return
+ if not self.file_path_directory:
+ self.file_path_directory = directory
+ if not self.file_path1_directory:
+ self.file_path1_directory = directory
+ if not self.file_path2_directory:
+ self.file_path2_directory = directory
+
+ self.municipality = self.get_full_path(self.file_path_directory, self.municipality_entry.text())
+ self.csv = self.get_full_path(self.file_path1_directory, self.csv_entry.text())
+ self.risk30_vp = self.get_full_path(self.file_path2_directory, self.risk30_vp_entry.text())
+
+ if not self.csv or not self.municipality or not self.risk30_vp:
+ QMessageBox.critical(self, "Error", "Please select or enter all input files!")
+ return
+
+ images = [self.municipality, self.risk30_vp]
+
+ # Check if all images have the same resolution
+ resolutions = [map_checker.get_image_resolution(img) for img in images]
+ if len(set(resolutions)) != 1:
+ QMessageBox.critical(None, "Error", "All the input raster images must have the same spatial resolution!")
+ return
+
+ # Check if all images have the same number of rows and columns
+ dimensions = [map_checker.get_image_dimensions(img) for img in images]
+ if len(set(dimensions)) != 1:
+ QMessageBox.critical(None, "Error",
+ "All the input raster images must have the same number of rows and columns!")
+ return
+
+ expected_deforestation = self.expected_entry.text()
+ if not expected_deforestation:
+ QMessageBox.critical(self, "Error", "Please enter the expected annual jurisdictional deforestation rate (ha/year)!")
+ return
+ try:
+ self.expected_deforestation = float(expected_deforestation)
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Expected annual jurisdictional deforestation rate should be a valid number!")
+ return
+
+ out_fn1 = self.image1_entry.text()
+ if not out_fn1:
+ QMessageBox.critical(self, "Error", "Please enter the name of Prediction Modeling Region Map in VP!")
+ return
+
+ if not (out_fn1.endswith('.tif') or out_fn1.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Prediction Modeling Region Map in VP!")
+ return
+
+ out_fn2 = self.image2_entry.text()
+ if not out_fn2:
+ QMessageBox.critical(self, "Error", "Please enter the name of Adjusted Prediction Density Map in VP!")
+ return
+
+ if not (out_fn2.endswith('.tif') or out_fn2.endswith('.rst')):
+ QMessageBox.critical(self, "Error",
+ "Please enter .rst or .tif extension in the name of Adjusted Prediction Density Map in VP!")
+ return
+
+ max_iterations = self.iteration_entry.text()
+ if not max_iterations:
+ QMessageBox.critical(self, "Error", "Please enter the max iterations! The suggestion number is 5.")
+ return
+ try:
+ self.max_iterations = int(max_iterations)
+ except ValueError:
+ QMessageBox.critical(self, "Error", "Max iteration value should be a valid number!")
+ return
+
+ # Show "Processing" message
+ processing_message = "Processing data..."
+ self.progressDialog = QProgressDialog(processing_message, None, 0, 100, self)
+
+ # Change the font size
+ font = QFont()
+ font.setPointSize(9)
+ self.progressDialog.setFont(font)
+
+ self.progressDialog.setWindowTitle("Processing")
+ self.progressDialog.setWindowModality(Qt.WindowModal)
+ self.progressDialog.setMinimumDuration(0)
+ self.progressDialog.resize(400, 300)
+ self.progressDialog.show()
+ QApplication.processEvents()
+
+ try:
+ id_difference,iteration_count = self.allocation_tool.execute_workflow_vp(directory, self.max_iterations,
+ self.csv,
+ self.municipality,
+ self.expected_deforestation,
+ self.risk30_vp, out_fn1,out_fn2)
+
+ if id_difference.size > 0:
+ QMessageBox.warning(self, " Warning ", f"Modeling Region ID {','.join(map(str, id_difference))} do not exist in the Historical Reference Period. A new CSV has been created for the HRP where relative frequencies for missing bins have been estimated from corresponding vulnerability zones over the entire jurisdiction.")
+ if iteration_count > int(max_iterations):
+ QMessageBox.warning(self, " Warning ",
+ f"Maximum iterations limit reached. Please increase the Maximum Iterations and try running the tool again.")
+ else:
+ QMessageBox.information(self, "Processing Completed", "Processing completed!")
+ self.progressDialog.close()
+
+ except Exception:
+ self.progressDialog.close()
+ tb = traceback.format_exc()
+
+ error_box = QMessageBox(self)
+ error_box.setIcon(QMessageBox.Critical)
+ error_box.setWindowTitle("Error")
+ error_box.setText(f"An Error Occurred During Processing")
+ error_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
+ error_box.setDetailedText(tb)
+
+ details_frame = error_box.findChild(QTextEdit)
+ if details_frame:
+ details_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ details_frame.setMaximumHeight(800)
+ details_frame.setMaximumWidth(1000)
+ details_frame.setMinimumHeight(400)
+ details_frame.setMinimumWidth(600)
+
+ error_box_layout = error_box.layout()
+ error_box_layout.setContentsMargins(10, 10, 10, 10)
+
+ copy_button = QPushButton("Copy Error Message")
+ error_box.addButton(copy_button, QMessageBox.ActionRole)
+ def copy_error_txt():
+ QApplication.clipboard().setText(tb)
+ copy_button.clicked.connect(copy_error_txt)
+ error_box.exec_()
+
+ def update_progress(self, value):
+ # Update QProgressDialog with the new value
+ if self.progressDialog is not None:
+ self.progressDialog.setValue(value)
+
+class CentralDataStore:
+ def __init__(self):
+ self.NRT = None
+ self.directory = None
+
+class MapChecker:
+ def __init__(self):
+ self.image=None
+ self.arr=None
+ self.in_fn=None
+
+ def get_image_resolution(self,image):
+ in_ds = gdal.Open(image)
+ P = in_ds.GetGeoTransform()[1]
+ return P
+
+ def get_image_dimensions(self, image):
+ dataset = gdal.Open(image)
+ cols = dataset.RasterXSize
+ rows = dataset.RasterYSize
+ return rows, cols
+
+ def get_image_datatype(self, image):
+ in_ds = gdal.Open(image)
+ in_band = in_ds.GetRasterBand(1)
+ datatype = gdal.GetDataTypeName(in_band.DataType)
+ return datatype
+
+ def get_image_max_min(self, image):
+ in_ds = gdal.Open(image)
+ in_band = in_ds.GetRasterBand(1)
+ min, max= in_band.ComputeRasterMinMax()
+ return min, max
+
+ def find_unique_values(self, arr, limit=2):
+ unique_values = set()
+ for value in np.nditer(arr):
+ unique_values.add(value.item())
+ if len(unique_values) > limit:
+ return False
+ return True
+
+ def check_binary_map(self, in_fn):
+ '''
+ Check if input image is binary map
+ :param in_fn: input image
+ :return: True if the file is a binary map, False otherwise
+ '''
+ file_extension = in_fn.split('.')[-1].lower()
+ file_name, _ = os.path.splitext(in_fn)
+ if file_extension == 'rst':
+ with open(file_name + '.rdc', 'r') as read_file:
+ rdc_content = read_file.read().lower()
+ byte_or_integer_binary = "data type : byte" in rdc_content or (
+ "data type : integer" in rdc_content and "min. value : 0" in rdc_content and "max. value : 1" in rdc_content)
+ float_binary = "data type : real" in rdc_content and "min. value : 0.0000000" in rdc_content and "max. value : 1.0000000" in rdc_content
+ elif file_extension == 'tif':
+ datatype = self.get_image_datatype(in_fn)
+ min_val, max_val = self.get_image_max_min(in_fn)
+ byte_or_integer_binary = datatype in ['Byte', 'CInt16', 'CInt32', 'Int16', 'Int32', 'UInt16',
+ 'UInt32'] and max_val == 1 and min_val == 0
+ float_binary = datatype in ['Float32', 'Float64', 'CFloat32', 'CFloat64'] and max_val == 1.0000000 and min_val == 0.0000000
+
+ if byte_or_integer_binary or (float_binary):
+ # For float_binary, use find_unique_values function to check if data only have two unique values [0.0000000, 1.0000000].
+ if float_binary:
+ in_ds = gdal.Open(in_fn)
+ in_band = in_ds.GetRasterBand(1)
+ arr = in_band.ReadAsArray()
+ # If more than two unique values are found, it's not a binary map, return False.
+ if not self.find_unique_values(arr, 2):
+ return False
+ # Binary map: byte_or_integer_binary or float_binary with two unique values [0.0000000, 1.0000000], it returns True.
+ return True
+ # For any other scenario, it returns False.
+ return False
+
+if __name__ == "__main__":
+ # main
+ app = QApplication(sys.argv)
+ # Load custom fonts
+ font_id = QFontDatabase.addApplicationFont(str(Path(PureWindowsPath("font\\AvenirNextLTPro-DemiCn.otf"))))
+
+ intro = IntroScreen()
+ # Create a global instance of this store
+ central_data_store = CentralDataStore()
+ map_checker = MapChecker()
+
+ widget = QtWidgets.QStackedWidget()
+ widget.addWidget(intro)
+ widget.setFixedHeight(1000)
+ widget.setFixedWidth(1800)
+ widget.show()
+
+ try:
+ sys.exit(app.exec_())
+ except:
print("Exiting")
\ No newline at end of file
diff --git a/model_evaluation.py b/model_evaluation.py
index 559c802..5b7db9b 100644
--- a/model_evaluation.py
+++ b/model_evaluation.py
@@ -1,756 +1,769 @@
-import os
-import sys
-from osgeo import gdal, osr, ogr
-from osgeo.gdalconst import *
-import matplotlib.pyplot as plt
-import numpy as np
-from scipy.spatial import Voronoi
-import scipy.stats as stats
-import geopandas as gpd
-import shapely
-import seaborn as sns
-from shapely.geometry import Point
-import pandas as pd
-from PyQt5.QtCore import QObject, pyqtSignal
-import shutil
-from geopandas import GeoDataFrame
-import plotly.graph_objects as go
-import plotly.io as pio
-
-# GDAL exceptions
-gdal.UseExceptions()
-
-class ModelEvaluation(QObject):
- progress_updated = pyqtSignal(int)
- def __init__(self):
- super(ModelEvaluation, self).__init__()
- self.data_folder = None
-
- def set_working_directory(self, directory: object) -> object:
- '''
- Set up the working directory
- :param directory: your local directory with all dat files
- '''
- self.progress_updated.emit(0)
- self.data_folder = directory
- os.chdir(self.data_folder)
-
- def image_to_array(self,image):
- # Set up a GDAL dataset
- in_ds = gdal.Open(image)
- # Set up a GDAL band
- in_band = in_ds.GetRasterBand(1)
- # Create Numpy Array1
- arr = in_band.ReadAsArray()
- return arr
-
- def array_to_image(self, in_fn, out_fn, data, data_type, nodata=None):
- '''
- Create image from array
- :param in_fn: datasource to copy projection and geotransform from
- :param out_fn:path to the file to create
- :param data:the NumPy array
- :param data_type:output data type
- :param nodata:optional NoData value
- :return:
- '''
- in_ds = gdal.Open(in_fn)
- output_format = out_fn.split('.')[-1].upper()
- if (output_format == 'TIF'):
- output_format = 'GTIFF'
- elif (output_format == 'RST'):
- output_format = 'rst'
- driver = gdal.GetDriverByName(output_format)
- out_ds = driver.Create(out_fn, in_ds.RasterXSize, in_ds.RasterYSize, 1, data_type, options=["BigTIFF=YES"])
- out_band = out_ds.GetRasterBand(1)
- out_ds.SetGeoTransform(in_ds.GetGeoTransform())
- out_ds.SetProjection(in_ds.GetProjection().encode('utf-8', 'backslashreplace').decode('utf-8'))
- if nodata is not None:
- out_band.SetNoDataValue(nodata)
- out_band.WriteArray(data)
- out_band.FlushCache()
- out_ds.FlushCache()
- return
-
- def replace_ref_system(self, in_fn, out_fn):
- '''
- RST raster format: correct reference system name in rdc file
- :param in_fn: datasource to copy correct projection name
- :param out_fn: rst raster file
- '''
- if out_fn.split('.')[-1] == 'rst':
- if in_fn.split('.')[-1] == 'rst':
- read_file_name, _ = os.path.splitext(in_fn)
- write_file_name, _ = os.path.splitext(out_fn)
- temp_file_path = 'rdc_temp.rdc'
-
- with open(read_file_name + '.rdc', 'r') as read_file:
- for line in read_file:
- if line.startswith("ref. system :"):
- correct_name = line
- break
-
- if correct_name:
- with open(write_file_name + '.rdc', 'r') as read_file, open(temp_file_path, 'w') as write_file:
- for line in read_file:
- if line.startswith("ref. system :"):
- write_file.write(correct_name)
- else:
- write_file.write(line)
-
- # Move the temp file to replace the original
- shutil.move(temp_file_path, write_file_name + '.rdc')
-
- elif in_fn.split('.')[-1] == 'tif':
- # Read projection information from the .tif file using GDAL
- dataset = gdal.Open(in_fn)
- projection = dataset.GetProjection()
- dataset = None
-
- # Extract the reference system name from the wkt projection
- ref_system_name = projection.split('PROJCS["')[1].split('"')[0]
-
- write_file_name, _ = os.path.splitext(out_fn)
- temp_file_path = 'rdc_temp.rdc'
-
- with open(write_file_name + '.rdc', 'r') as read_file, open(temp_file_path, 'w') as write_file:
- for line in read_file:
- if line.startswith("ref. system :"):
- write_file.write(f"ref. system : {ref_system_name}\n")
- else:
- write_file.write(line)
-
- shutil.move(temp_file_path, write_file_name + '.rdc')
-
- def replace_legend(self, out_fn):
- '''
- RST raster format: correct legend in rdc file of Combined Deforestation Review Map
- :param out_fn: rst raster file
- '''
- if out_fn.split('.')[-1] == 'rst':
- base_name, _ = os.path.splitext(out_fn)
- temp_file_path = 'rdc_temp.rdc'
-
- with open(base_name + '.rdc', 'r') as read_file, open(temp_file_path, 'w') as write_file:
- for line in read_file:
- if line.startswith("legend cats :"):
- write_file.write("legend cats : " + '3'+'\n')
- # Write the three new lines
- write_file.write("code 1 : "+"Forest at the start of HRP"+"\n")
- write_file.write("code 2 : "+"Deforestation within CAL"+"\n")
- write_file.write("code 3 : "+"Deforestation within CNF"+"\n")
- else:
- write_file.write(line)
- shutil.move(temp_file_path, base_name + '.rdc')
-
- def create_mask_polygon(self, mask):
- '''
- Create municipality mask polygon
- :param mask: mask of the jurisdiction (binary map)
- :return:
- '''
- in_ds = gdal.Open(mask)
- in_band = in_ds.GetRasterBand(1)
-
- # Set up osr spatial reference
- projection = in_ds.GetProjection().encode('utf-8', 'backslashreplace').decode('utf-8')
- self.progress_updated.emit(10)
- spatial_ref = osr.SpatialReference()
- spatial_ref.ImportFromWkt(projection)
-
- # Create a temporary shapefile to store all polygons
- temp_layername = "POLYGONIZED_MASK"
- driver = ogr.GetDriverByName("ESRI Shapefile")
- temp_ds = driver.CreateDataSource(temp_layername + ".shp")
- temp_layer = temp_ds.CreateLayer(temp_layername, srs=spatial_ref)
- gdal.Polygonize(in_band, in_band, temp_layer, -1, [], callback=None)
- self.progress_updated.emit(20)
- return
-
- def bbox_to_pixel_offsets(self,gt, bbox):
- '''
- https://gist.github.com/perrygeo/5667173
- '''
- originX = gt[0]
- originY = gt[3]
- pixel_width = gt[1]
- pixel_height = gt[5]
- x1 = int((bbox[0] - originX) / pixel_width)
- x2 = int((bbox[1] - originX) / pixel_width) + 1
-
- y1 = int((bbox[3] - originY) / pixel_height)
- y2 = int((bbox[2] - originY) / pixel_height) + 1
-
- xsize = x2 - x1
- ysize = y2 - y1
- return (x1, y1, xsize, ysize)
-
- def zonal_stats(self, vector_path, raster_path, nodata_value=None, global_src_extent=False):
- '''
- https://gist.github.com/perrygeo/5667173
- '''
- rds = gdal.Open(raster_path, GA_ReadOnly)
- assert (rds)
- rb = rds.GetRasterBand(1)
- rgt = rds.GetGeoTransform()
-
- if nodata_value:
- nodata_value = float(nodata_value)
- rb.SetNoDataValue(nodata_value)
-
- vds = ogr.Open(vector_path, GA_ReadOnly) # TODO maybe open update if we want to write stats
- assert (vds)
- vlyr = vds.GetLayer(0)
-
- # create an in-memory numpy array of the source raster data
- # covering the whole extent of the vector layer
- if global_src_extent:
- # use global source extent
- # useful only when disk IO or raster scanning inefficiencies are your limiting factor
- # advantage: reads raster data in one pass
- # disadvantage: large vector extents may have big memory requirements
- src_offset = self.bbox_to_pixel_offsets(rgt, vlyr.GetExtent())
- src_array = rb.ReadAsArray(*src_offset)
-
- # calculate new geotransform of the layer subset
- new_gt = (
- (rgt[0] + (src_offset[0] * rgt[1])),
- rgt[1],
- 0.0,
- (rgt[3] + (src_offset[1] * rgt[5])),
- 0.0,
- rgt[5]
- )
-
- mem_drv = ogr.GetDriverByName('Memory')
- driver = gdal.GetDriverByName('MEM')
-
- # Loop through vectors
- stats = []
- feat = vlyr.GetNextFeature()
- while feat is not None:
-
- if not global_src_extent:
- # use local source extent
- # fastest option when you have fast disks and well indexed raster (ie tiled Geotiff)
- # advantage: each feature uses the smallest raster chunk
- # disadvantage: lots of reads on the source raster
- src_offset = self.bbox_to_pixel_offsets(rgt, feat.geometry().GetEnvelope())
- src_array = rb.ReadAsArray(*src_offset)
-
- # calculate new geotransform of the feature subset
- new_gt = (
- (rgt[0] + (src_offset[0] * rgt[1])),
- rgt[1],
- 0.0,
- (rgt[3] + (src_offset[1] * rgt[5])),
- 0.0,
- rgt[5]
- )
-
- # Create a temporary vector layer in memory
- mem_ds = mem_drv.CreateDataSource('out')
- mem_layer = mem_ds.CreateLayer('poly', None, ogr.wkbPolygon)
- mem_layer.CreateFeature(feat.Clone())
-
- # Rasterize it
- rvds = driver.Create('', src_offset[2], src_offset[3], 1, gdal.GDT_Byte)
- rvds.SetGeoTransform(new_gt)
- gdal.RasterizeLayer(rvds, [1], mem_layer, burn_values=[1])
- rv_array = rvds.ReadAsArray()
-
- # Mask the source data array with our current feature
- # we take the logical_not to flip 0<->1 to get the correct mask effect
- # we also mask out nodata values explictly
- masked = np.ma.MaskedArray(
- src_array,
- mask=np.logical_or(
- src_array == nodata_value,
- np.logical_not(rv_array)
- )
- )
-
- feature_stats = {
- 'sum': float(masked.sum())}
-
- stats.append(feature_stats)
-
- rvds = None
- mem_ds = None
- feat = vlyr.GetNextFeature()
-
- vds = None
- rds = None
- return stats
-
- def vector_to_raster(self,vector_fn,in_fn,raster_fn,data_type, nodata=None):
- '''
- Create residual raster image from vector file
- :param vector_fn: vector datasource
- :param in_fn: datasource to copy projection and geotransform from
- :param raster_fn:path to create raster file
- :param data_type:output data type
- :param nodata:optional NoData value
- :return
- '''
- # Open the vector data source
- source_ds = ogr.Open(vector_fn)
- source_layer = source_ds.GetLayer()
-
- in_ds = gdal.Open(in_fn)
- output_format = raster_fn.split('.')[-1].upper()
- if (output_format == 'TIF'):
- output_format = 'GTIFF'
- elif (output_format == 'RST'):
- output_format = 'rst'
- driver = gdal.GetDriverByName(output_format)
- out_ds = driver.Create(raster_fn, in_ds.RasterXSize, in_ds.RasterYSize, 1, data_type, options=["BigTIFF=YES"])
- out_band = out_ds.GetRasterBand(1)
- out_ds.SetGeoTransform(in_ds.GetGeoTransform())
-
- out_ds.SetProjection(in_ds.GetProjection().encode('utf-8', 'backslashreplace').decode('utf-8'))
-
- if nodata is not None:
- out_band.SetNoDataValue(nodata)
- # Rasterize
- gdal.RasterizeLayer(out_ds, [1], source_layer, options=["ATTRIBUTE=Residuals"])
-
- # Cleanup
- out_band.FlushCache()
- out_ds.FlushCache()
- return
-
- def remove_edge_cells(self, full_voronoi_grid: GeoDataFrame, area_mask: GeoDataFrame,
- area_percentile_threshold: float) -> GeoDataFrame:
- '''
- Ensure thiessen polygon cells retain percentile threshold of maximum size after intersection with mask of the jurisdiction
- :param full_voronoi_grid: thiessen polygon dataframe
- :param area_mask: mask of the jurisdiction
- :param area_percentile_threshold: area percentile threshold
- :return thiessen_gdf: result dataframe
- '''
- thiessen_gdf = gpd.overlay(full_voronoi_grid, area_mask, how="intersection", keep_geom_type=False)
- # get area of each polygon
- thiessen_gdf["area"] = thiessen_gdf.area
-
- max_area = thiessen_gdf["area"].max()
- # using the area, calculate size of cell compared to max
- thiessen_gdf["percentcell"] = thiessen_gdf["area"] / max_area
-
- # select cells with more than thresh% of their area within the mask
- thiessen_gdf = thiessen_gdf[thiessen_gdf["percentcell"] > area_percentile_threshold]
-
- return thiessen_gdf
-
- def create_thiessen_polygon (self, grid_area, mask, density, deforestation, out_fn, raster_fn):
- '''
- Create thiessen polygon
- :param grid_area: assessment grid cell area or 100,000 (ha)
- :param mask: mask of the jurisdiction (binary map)
- :param density: adjusted prediction density map
- :param deforestation:Deforestation Map during the HRP
- :param csv_name:Name of performance chart
- :return clipped_gdf: thiessen polygon dataframe
- '''
- ## Create sample points:
- # Open the Polygonized_Mask shapefile
- mask_df = gpd.GeoDataFrame.from_file('POLYGONIZED_MASK.shp')
-
- # Calculate grid size
- in_ds = gdal.Open(mask)
- grid_size = int(np.sqrt(grid_area * 10000)) // int(in_ds.GetGeoTransform()[1])
-
- # Systematic Sampling
- sample_points = []
- for y in range(-1 * grid_size, in_ds.RasterYSize + 1 * grid_size, grid_size):
- for x in range(-1 * grid_size, in_ds.RasterXSize + 1 * grid_size, grid_size):
- # Convert raster coordinates to geographic coordinates
- geo_x = in_ds.GetGeoTransform()[0] + x * in_ds.GetGeoTransform()[1]
- geo_y = in_ds.GetGeoTransform()[3] + y * in_ds.GetGeoTransform()[5]
- sample_points.append((geo_x, geo_y))
-
- # Convert sample_points list to DataFrame
- df = pd.DataFrame(sample_points, columns=['geo_x', 'geo_y'])
- df['coords'] = list(zip(df['geo_x'], df['geo_y']))
- df['coords_P'] = df['coords'].apply(Point)
- points_df = gpd.GeoDataFrame(df, geometry='coords_P', crs=mask_df.crs)
-
- # Convert the 'coords' column to a numpy array
- coords = np.array(points_df['coords'].tolist())
-
- ## Create thiessen polygon
- vor = Voronoi(points=coords)
-
- # Polygonize the line ridge is not infinity
- lines = [shapely.geometry.LineString(vor.vertices[line]) for line in
- vor.ridge_vertices if -1 not in line]
-
- polys = shapely.ops.polygonize(lines)
-
- # Convert Voronoi polygons (polys) into a GeoDataFrame.
- voronois = gpd.GeoDataFrame(geometry=gpd.GeoSeries(polys), crs=mask_df.crs)
- self.progress_updated.emit(30)
-
- # Ensure Thiessen Polygon cells retain 99.9% of maximum size after intersection with study area
- thiessen_gdf = self.remove_edge_cells(voronois, mask_df, 0.999)
-
- self.progress_updated.emit(40)
-
- # Extract polygons and multipolygons from the entire thiessen_gdf (including GeometryCollections)
- extracted_gdf = thiessen_gdf['geometry'].apply(
- lambda geom: [g for g in geom.geoms if
- g.geom_type in ['Polygon', 'MultiPolygon']] if geom.geom_type == 'GeometryCollection' else [
- geom]
- ).explode().reset_index(drop=True)
-
- clipped_gdf = gpd.GeoDataFrame({'geometry': extracted_gdf}, crs=thiessen_gdf.crs)
-
- # Calculate area in hectares
- clipped_gdf['Area_ha'] = clipped_gdf['geometry'].area / 10000
-
- ## Calculate zonal statistics
-
- ## Convert clipped_gdf to shapefile
- vector_temp_path = "temp_vector.shp"
- clipped_gdf.to_file(vector_temp_path)
-
- # Actual Deforestation(ha)
- stats = self.zonal_stats(vector_temp_path, deforestation, nodata_value=0)
-
- self.progress_updated.emit(50)
-
- # Calculate areal_resolution_of_map_pixels
- in_ds4 = gdal.Open(density)
- P1 = in_ds4.GetGeoTransform()[1]
- P2 = abs(in_ds4.GetGeoTransform()[5])
- areal_resolution_of_map_pixels = P1 * P2 / 10000
-
- # Add the results back to the GeoDataFrame
- clipped_gdf['Actual Deforestation(ha)'] = [(item['sum'] if item['sum'] is not None else 0) * areal_resolution_of_map_pixels for item in stats]
- self.progress_updated.emit(60)
-
- # Predicted Deforestation(ha)
- stats1 = self.zonal_stats(vector_temp_path, density, nodata_value=0)
- self.progress_updated.emit(70)
-
- clipped_gdf['Predicted Deforestation(ha)'] = [(item['sum'] if item['sum'] is not None else 0) for item in stats1]
-
- # ID
- clipped_gdf['ID'] = range(1, len(clipped_gdf) + 1)
-
- # Replace NaN or blank values with '0'
- columns_to_fill = ['Actual Deforestation(ha)', 'Predicted Deforestation(ha)']
- for column in columns_to_fill:
- clipped_gdf[column] = clipped_gdf[column].fillna(0)
-
- # Calculate residuals
- clipped_gdf['Residuals(ha)'] = clipped_gdf['Predicted Deforestation(ha)'] - clipped_gdf['Actual Deforestation(ha)']
-
- # Export to csv
- csv_file_path = out_fn.split('.')[0]+'.csv'
- clipped_gdf.drop('geometry', axis=1).to_csv(csv_file_path, columns=['ID', 'Actual Deforestation(ha)', 'Predicted Deforestation(ha)','Residuals(ha)'],
- index=False)
-
- # Rename columns title for shapefile
- clipped_gdf = clipped_gdf.rename(columns={'Predicted Deforestation(ha)': 'PredDef',
- 'Actual Deforestation(ha)': 'ActualDef',
- 'Residuals(ha)':'Residuals'})
-
- # Save the updated GeoDataFrame back to a shapefile
-
- tp_file_path = out_fn.split('.')[0]+'.shp'
- clipped_gdf.to_file(tp_file_path)
-
- # Create residual map
- self.vector_to_raster(tp_file_path, mask, raster_fn, gdal.GDT_Float32,-1)
-
- return clipped_gdf
-
- def create_deforestation_map (self, fmask, deforestation_cal, deforestation_cnf, out_fn_def):
- self.progress_updated.emit(80)
- arr_fmask = self.image_to_array(fmask)
- arr_def_cal = self.image_to_array(deforestation_cal)
- arr_def_cnf = self.image_to_array(deforestation_cnf)
-
- deforestation_arr=np.copy(arr_fmask)
-
- deforestation_arr[arr_def_cnf == 1] = 3
- deforestation_arr[(arr_def_cnf == 0) & (arr_def_cal == 1)] = 2
- deforestation_arr[(arr_def_cnf == 0) & (arr_def_cal == 0) & (arr_fmask == 1)] = 1
-
- #write deforestation_map
- self.array_to_image(fmask, out_fn_def, deforestation_arr, gdal.GDT_Int16, -1)
-
- return
-
- def create_plot(self, grid_area, clipped_gdf, title, out_fn,xmax=None, ymax=None):
- '''
- Create plot and save to local directory
- :param grid_area: assessment grid cell area or 100,000 (ha)
- :param clipped_gdf: thiessen_polygon geo-dataframe
- :param title:plot title
- :param out_fn: plot path
- :param xmax: maximum x-axis value
- :param ymax: maximum y-axis value
- :return
- '''
- self.progress_updated.emit(90)
- # Set Seaborn Style
- sns.set()
-
- # prepare the X/Y data
- X = np.array(clipped_gdf['ActualDef'], dtype=np.float32)
- Y = np.array(clipped_gdf['PredDef'], dtype=np.float32)
- ids = np.asarray(clipped_gdf["ID"])
-
- # Set a proportion to extend the limits
- extension_f = 0.1
-
- # Check if lim is string and "default"
- if isinstance(xmax, str) and xmax.lower() == "default":
- xmax = max(X) * (1 + extension_f)
- else:
- xmax = float(xmax)
-
- if isinstance(ymax, str) and ymax.lower() == "default":
- ymax = max(Y) * (1 + extension_f)
- else:
- ymax = float(ymax)
-
- # Set a new X range from 0 to the xmax
- X_extended = np.linspace(0, xmax, 500)
-
- ## Perform linear regression
- slope, intercept, _, _, _ = stats.linregress(X, Y)
-
- # Create the equation string
- equation = f'Y = {slope:.4f} * X + {intercept:.2f}'
-
- # Calculate the trend line
- trend_line = slope * X_extended + intercept
-
- # 1-to-1 Line
- one_to_one_line = X_extended
-
- ## Calculate R square
- # Get the correlation coefficient
- r = np.corrcoef(X, Y)[0, 1]
- # Square the correlation coefficient
- r_squared = r ** 2
-
- ##Calculate MedAE
- distance_arr = [abs(X[i] - Y[i]) for i in range(len(X))]
- MedAE = np.median(distance_arr)
-
- ## Calculate MedAE percent
- MedAE_percent = (MedAE / int(grid_area)) * 100
-
- ## Calculate MAE
- MAE=np.sum(distance_arr)/len(X)
- MAE_percent = (MAE / int(grid_area)) * 100
-
- ## Calculate the IoU
- # agreement=intersection: Minimum of actual and predicted deforestation for each dot and add them all
- agree_arr = [min(X[i],Y[i]) for i in range(len(X))]
- agree = np.sum(agree_arr)
-
- # union
- union_arr = [max(X[i],Y[i]) for i in range(len(X))]
- union = np.sum(union_arr)
-
- # IoU
- iou = 0 if union == 0 else agree / union * 100
-
- ## Calculate the Difference
- # Absolute value of predicted deforestation minus actual deforestation of each point and add them all
- difference=np.sum(distance_arr)
-
- # Set the figure size
- plt.figure(figsize=(8, 6))
-
- # Create a scatter plot
- plt.scatter(clipped_gdf['ActualDef'], clipped_gdf['PredDef'], color='steelblue', alpha=0.5, linewidth=1.0, s=50)
-
- # Add labels and title
- plt.xlabel('Actual Deforestation (ha)', color='black', fontweight='bold', labelpad=10)
- plt.ylabel('Predicted Deforestation (ha)', color='black', fontweight='bold', labelpad=10)
- plt.title(title, color='firebrick', fontweight='bold', fontsize=20, pad=20)
-
- # Plot the trend line
- plt.plot(X_extended, trend_line, color='mediumseagreen', linestyle='-', label='OLS Line')
-
- # Plot a 1-to-1 line
- plt.plot(X_extended, one_to_one_line, color='crimson', linestyle='--',label='1:1 Line')
-
- ## Theil-Sen Regressor
- # Fit Theil-Sen Regressor
- # Compute Theil-Sen estimator
- ts_slope, ts_intercept, _, _ = stats.theilslopes(Y, X)
-
- # Generate predictions
- y_pred = ts_slope * X_extended + ts_intercept
-
- # Equation of the line
- ts_equation = f'Y = {ts_slope:.4f} * X + {ts_intercept:.2f}'
-
- # Plot Theil-Sen Line
- plt.plot(X_extended, y_pred, color='orange', linestyle='-', label='Theil-Sen Line')
-
- # Add a legend in the bottom right position
- plt.legend(loc='lower right')
-
- plt.xlim([0, xmax])
- plt.ylim([0, ymax])
-
- text_x_pos = ymax * 0.05
- text_y_start_pos = ymax * 0.9
- text_y_gap = ymax * 0.05
-
- # Adjust plt texts with the new calculated positions
- plt.text(text_x_pos, text_y_start_pos, f'Theil-Sen : {ts_equation}', fontsize=11,
- color='black')
- plt.text(text_x_pos, text_y_start_pos - text_y_gap, f'OLS : {equation}', fontsize=11, color='black')
- plt.text(text_x_pos, text_y_start_pos - 2 * text_y_gap, f'Samples = {len(X)}', fontsize=11, color='black')
- plt.text(text_x_pos, text_y_start_pos - 3 * text_y_gap, f'R^2 = {r_squared:.4f}', fontsize=11, color='black')
- plt.text(text_x_pos, text_y_start_pos - 4 * text_y_gap, f'MedAE = {MedAE:.2f} ({MedAE_percent:.2f}%)',
- fontsize=11, color='black')
- plt.text(text_x_pos, text_y_start_pos - 5 * text_y_gap, f'IoU = {iou:.2f}%',fontsize=11, color='black')
-
- ## Save metrics to txt file
- base, ext = os.path.splitext(out_fn)
- txt_out = base + ".txt"
- lines_to_write = [
- f"OLS: {equation}\n",
- f"Theil Sen: {ts_equation}\n",
- f"Samples = {len(X)}\n",
- f"R^2 = {r_squared:.4f}\n",
- f"MedAE = {MedAE:.2f} ({MedAE_percent:.2f}%)\n",
- f"MAE = {MAE:.2f} ({MAE_percent:.2f}%)\n",
- f"IoU : {iou:.2f}%\n",
- f"Agreement : {agree:.2f}\n",
- f"Difference : {difference:.2f}\n",
- ]
-
- with open(txt_out, "w") as file:
- file.writelines(lines_to_write)
-
- # x, yticks
- plt.yticks(fontsize=10, color='dimgrey')
- plt.xticks(fontsize=10, color='dimgrey')
-
- # Save the plot
- plt.savefig(out_fn)
-
- ## html
- # Build Plotly figure
- fig = go.Figure()
-
- # Hover
- fig.add_trace(go.Scatter(
- x=X, y=Y, mode="markers",
- name="Cells",
- customdata=np.stack([ids, X, Y, agree_arr,distance_arr], axis=-1),
- hovertemplate="ID: %{customdata[0]}
Actual: %{x:.2f} ha
Predicted: %{y:.2f} ha
Agreement: %{customdata[3]}
Difference: %{customdata[4]}
",
- marker=dict(size=8, line=dict(width=0.5), color="steelblue", opacity=0.5), showlegend=False
- ))
-
- # OLS line trace
- fig.add_trace(go.Scatter(
- x=X_extended, y=trend_line, mode="lines",
- name="OLS Line",
- line=dict(color="mediumseagreen", dash="solid")
- , hoverinfo="skip", hovertemplate=None
- ))
-
- # Theil–Sen line trace
- fig.add_trace(go.Scatter(
- x=X_extended, y=y_pred, mode="lines",
- name="Theil–Sen Line",
- line=dict(color="orange", dash="solid")
- , hoverinfo="skip", hovertemplate=None
- ))
-
- # 1:1 line trace
- fig.add_trace(go.Scatter(
- x=X_extended, y=one_to_one_line, mode="lines",
- name="1:1 Line",
- line=dict(color="crimson", dash="dash")
- , hoverinfo="skip", hovertemplate=None
- ))
-
- # Layout setting
- fig.update_layout(
- title=dict(
- text=f"{title}",
- font=dict(color="firebrick", size=20),
- x=0.5, xanchor="center"
- ),
- xaxis=dict(
- title=dict(text="Actual Deforestation (ha)",
- font=dict(color="black"),
- standoff=10),
- tickfont=dict(size=10, color="dimgrey"),
- range=[0, xmax],
- constrain="domain"
- ),
- yaxis=dict(
- title=dict(text="Predicted Deforestation (ha)",
- font=dict(color="black"),
- standoff=10),
- tickfont=dict(size=10, color="dimgrey"),
- range=[0, ymax]
- ),
- hovermode="closest",
- legend=dict(
- x=0.99, y=0.01, xanchor="right", yanchor="bottom", orientation="v"
- ),
- margin=dict(l=60, r=30, t=80, b=60),
- paper_bgcolor= 'rgb(255,255,255)',
- plot_bgcolor= 'rgb(234,234,242)',
- )
-
- # Annotations
- stats_text = (
- f"Theil–Sen: {ts_equation}
"
- f"OLS: {equation}
"
- f"Samples = {len(X)}
"
- f"R² = {r_squared:.4f}
"
- f"MedAE = {MedAE:.2f} ({MedAE_percent:.2f}%)
"
- f"IoU = {iou:.2f}%
"
- )
-
- fig.add_annotation(
- x=0.02, y=0.98, xref="paper", yref="paper", xanchor="left", yanchor="top",
- text=stats_text, showarrow=False, align="left"
- )
-
- # Save a HTML
- html_out = base + ".html"
-
- pio.write_html(fig, file=html_out, include_plotlyjs=True, full_html=True)
-
- return
-
- def remove_temp_files(self):
- # Files to check for and delete
- mask_file = 'mask'
- shapefiles_to_delete = ["TEMP_POLYGONIZED","POLYGONIZED_MASK","thiessen_polygon_temp","temp_vector"]
-
- # Shapefile associated extensions
- mask_file_extensions =[".tif",".rst",".rdc",".RST",".RST.aux.xml",".ref"]
- shapefile_extensions = [".shp", ".shx", ".dbf", ".prj", ".sbn", ".sbx",".cpg", ".shp.xml"]
-
- # Delete mask files
- for mask_ext in mask_file_extensions:
- mask_filename = f"{mask_file}{mask_ext}"
- if os.path.exists(mask_filename):
- os.remove(mask_filename)
-
- # Delete shapefiles with associated extensions
- for shp_base in shapefiles_to_delete:
- for ext in shapefile_extensions:
- full_filename = f"{shp_base}{ext}"
- if os.path.exists(full_filename):
- os.remove(full_filename)
- self.progress_updated.emit(100)
- return
\ No newline at end of file
+import os
+import sys
+from osgeo import gdal, osr, ogr
+from osgeo.gdalconst import *
+import matplotlib.pyplot as plt
+import numpy as np
+from scipy.spatial import Voronoi
+import scipy.stats as stats
+import geopandas as gpd
+import shapely
+import seaborn as sns
+from shapely.geometry import Point
+import pandas as pd
+from PyQt5.QtCore import QObject, pyqtSignal
+import shutil
+from geopandas import GeoDataFrame
+import plotly.graph_objects as go
+import plotly.io as pio
+
+# GDAL exceptions
+gdal.UseExceptions()
+
+class ModelEvaluation(QObject):
+ progress_updated = pyqtSignal(int)
+ def __init__(self):
+ super(ModelEvaluation, self).__init__()
+ self.data_folder = None
+
+ def set_working_directory(self, directory: object) -> object:
+ '''
+ Set up the working directory
+ :param directory: your local directory with all dat files
+ '''
+ self.progress_updated.emit(0)
+ self.data_folder = directory
+ os.chdir(self.data_folder)
+
+ def image_to_array(self,image):
+ # Set up a GDAL dataset
+ in_ds = gdal.Open(image)
+ # Set up a GDAL band
+ in_band = in_ds.GetRasterBand(1)
+ # Create Numpy Array1
+ arr = in_band.ReadAsArray()
+ return arr
+
+ def array_to_image(self, in_fn, out_fn, data, data_type, nodata=None):
+ '''
+ Create image from array
+ :param in_fn: datasource to copy projection and geotransform from
+ :param out_fn:path to the file to create
+ :param data:the NumPy array
+ :param data_type:output data type
+ :param nodata:optional NoData value
+ :return:
+ '''
+ in_ds = gdal.Open(in_fn)
+ output_format = out_fn.split('.')[-1].upper()
+ if (output_format == 'TIF'):
+ output_format = 'GTIFF'
+ elif (output_format == 'RST'):
+ output_format = 'rst'
+ driver = gdal.GetDriverByName(output_format)
+ out_ds = driver.Create(out_fn, in_ds.RasterXSize, in_ds.RasterYSize, 1, data_type, options=["BigTIFF=YES"])
+ out_band = out_ds.GetRasterBand(1)
+ out_ds.SetGeoTransform(in_ds.GetGeoTransform())
+ out_ds.SetProjection(in_ds.GetProjection().encode('utf-8', 'backslashreplace').decode('utf-8'))
+ if nodata is not None:
+ out_band.SetNoDataValue(nodata)
+ out_band.WriteArray(data)
+ out_band.FlushCache()
+ out_ds.FlushCache()
+ return
+
+ def replace_ref_system(self, in_fn, out_fn):
+ '''
+ RST raster format: correct reference system name in rdc file
+ :param in_fn: datasource to copy correct projection name
+ :param out_fn: rst raster file
+ '''
+ if out_fn.split('.')[-1] == 'rst':
+ if in_fn.split('.')[-1] == 'rst':
+ read_file_name, _ = os.path.splitext(in_fn)
+ write_file_name, _ = os.path.splitext(out_fn)
+ temp_file_path = 'rdc_temp.rdc'
+
+ with open(read_file_name + '.rdc', 'r') as read_file:
+ for line in read_file:
+ if line.startswith("ref. system :"):
+ correct_name = line
+ break
+
+ if correct_name:
+ with open(write_file_name + '.rdc', 'r') as read_file, open(temp_file_path, 'w') as write_file:
+ for line in read_file:
+ if line.startswith("ref. system :"):
+ write_file.write(correct_name)
+ else:
+ write_file.write(line)
+
+ # Move the temp file to replace the original
+ shutil.move(temp_file_path, write_file_name + '.rdc')
+
+ elif in_fn.split('.')[-1] == 'tif':
+ # Read projection information from the .tif file using GDAL
+ dataset = gdal.Open(in_fn)
+ projection = dataset.GetProjection()
+ dataset = None
+
+ # Extract the reference system name from the wkt projection
+ ref_system_name = projection.split('PROJCS["')[1].split('"')[0]
+
+ write_file_name, _ = os.path.splitext(out_fn)
+ temp_file_path = 'rdc_temp.rdc'
+
+ with open(write_file_name + '.rdc', 'r') as read_file, open(temp_file_path, 'w') as write_file:
+ for line in read_file:
+ if line.startswith("ref. system :"):
+ write_file.write(f"ref. system : {ref_system_name}\n")
+ else:
+ write_file.write(line)
+
+ shutil.move(temp_file_path, write_file_name + '.rdc')
+
+ def replace_legend(self, out_fn):
+ '''
+ RST raster format: correct legend in rdc file of Combined Deforestation Review Map
+ :param out_fn: rst raster file
+ '''
+ if out_fn.split('.')[-1] == 'rst':
+ base_name, _ = os.path.splitext(out_fn)
+ temp_file_path = 'rdc_temp.rdc'
+
+ with open(base_name + '.rdc', 'r') as read_file, open(temp_file_path, 'w') as write_file:
+ for line in read_file:
+ if line.startswith("legend cats :"):
+ """
+ write_file.write("legend cats : " + '3'+'\n')
+ # Write the three new lines
+ write_file.write("code 1 : "+"Forest at the start of HRP"+"\n")
+ write_file.write("code 2 : "+"Deforestation within CAL"+"\n")
+ write_file.write("code 3 : "+"Deforestation within CNF"+"\n")
+ """
+ write_file.write("legend cats : " + '4'+'\n')
+ # Write the four new lines of legend categories
+ write_file.write("code 1 : "+"Stable Forest (HRP)"+"\n")
+ write_file.write("code 2 : "+"Forest Loss within CAL"+"\n")
+ write_file.write("code 3 : "+"Forest Loss within CNF"+"\n")
+ write_file.write("code 4 : "+"Stable Nonforest (HRP)"+"\n")
+ else:
+ write_file.write(line)
+ shutil.move(temp_file_path, base_name + '.rdc')
+
+ def create_mask_polygon(self, mask):
+ '''
+ Create municipality mask polygon
+ :param mask: mask of the jurisdiction (binary map)
+ :return:
+ '''
+ in_ds = gdal.Open(mask)
+ in_band = in_ds.GetRasterBand(1)
+
+ # Set up osr spatial reference
+ projection = in_ds.GetProjection().encode('utf-8', 'backslashreplace').decode('utf-8')
+ self.progress_updated.emit(10)
+ spatial_ref = osr.SpatialReference()
+ spatial_ref.ImportFromWkt(projection)
+
+ # Create a temporary shapefile to store all polygons
+ temp_layername = "POLYGONIZED_MASK"
+ driver = ogr.GetDriverByName("ESRI Shapefile")
+ temp_ds = driver.CreateDataSource(temp_layername + ".shp")
+ temp_layer = temp_ds.CreateLayer(temp_layername, srs=spatial_ref)
+ gdal.Polygonize(in_band, in_band, temp_layer, -1, [], callback=None)
+ self.progress_updated.emit(20)
+ return
+
+ def bbox_to_pixel_offsets(self,gt, bbox):
+ '''
+ https://gist.github.com/perrygeo/5667173
+ '''
+ originX = gt[0]
+ originY = gt[3]
+ pixel_width = gt[1]
+ pixel_height = gt[5]
+ x1 = int((bbox[0] - originX) / pixel_width)
+ x2 = int((bbox[1] - originX) / pixel_width) + 1
+
+ y1 = int((bbox[3] - originY) / pixel_height)
+ y2 = int((bbox[2] - originY) / pixel_height) + 1
+
+ xsize = x2 - x1
+ ysize = y2 - y1
+ return (x1, y1, xsize, ysize)
+
+ def zonal_stats(self, vector_path, raster_path, nodata_value=None, global_src_extent=False):
+ '''
+ https://gist.github.com/perrygeo/5667173
+ '''
+ rds = gdal.Open(raster_path, GA_ReadOnly)
+ assert (rds)
+ rb = rds.GetRasterBand(1)
+ rgt = rds.GetGeoTransform()
+
+ if nodata_value:
+ nodata_value = float(nodata_value)
+ rb.SetNoDataValue(nodata_value)
+
+ vds = ogr.Open(vector_path, GA_ReadOnly) # TODO maybe open update if we want to write stats
+ assert (vds)
+ vlyr = vds.GetLayer(0)
+
+ # create an in-memory numpy array of the source raster data
+ # covering the whole extent of the vector layer
+ if global_src_extent:
+ # use global source extent
+ # useful only when disk IO or raster scanning inefficiencies are your limiting factor
+ # advantage: reads raster data in one pass
+ # disadvantage: large vector extents may have big memory requirements
+ src_offset = self.bbox_to_pixel_offsets(rgt, vlyr.GetExtent())
+ src_array = rb.ReadAsArray(*src_offset)
+
+ # calculate new geotransform of the layer subset
+ new_gt = (
+ (rgt[0] + (src_offset[0] * rgt[1])),
+ rgt[1],
+ 0.0,
+ (rgt[3] + (src_offset[1] * rgt[5])),
+ 0.0,
+ rgt[5]
+ )
+
+ mem_drv = ogr.GetDriverByName('Memory')
+ driver = gdal.GetDriverByName('MEM')
+
+ # Loop through vectors
+ stats = []
+ feat = vlyr.GetNextFeature()
+ while feat is not None:
+
+ if not global_src_extent:
+ # use local source extent
+ # fastest option when you have fast disks and well indexed raster (ie tiled Geotiff)
+ # advantage: each feature uses the smallest raster chunk
+ # disadvantage: lots of reads on the source raster
+ src_offset = self.bbox_to_pixel_offsets(rgt, feat.geometry().GetEnvelope())
+ src_array = rb.ReadAsArray(*src_offset)
+
+ # calculate new geotransform of the feature subset
+ new_gt = (
+ (rgt[0] + (src_offset[0] * rgt[1])),
+ rgt[1],
+ 0.0,
+ (rgt[3] + (src_offset[1] * rgt[5])),
+ 0.0,
+ rgt[5]
+ )
+
+ # Create a temporary vector layer in memory
+ mem_ds = mem_drv.CreateDataSource('out')
+ mem_layer = mem_ds.CreateLayer('poly', None, ogr.wkbPolygon)
+ mem_layer.CreateFeature(feat.Clone())
+
+ # Rasterize it
+ rvds = driver.Create('', src_offset[2], src_offset[3], 1, gdal.GDT_Byte)
+ rvds.SetGeoTransform(new_gt)
+ gdal.RasterizeLayer(rvds, [1], mem_layer, burn_values=[1])
+ rv_array = rvds.ReadAsArray()
+
+ # Mask the source data array with our current feature
+ # we take the logical_not to flip 0<->1 to get the correct mask effect
+ # we also mask out nodata values explictly
+ masked = np.ma.MaskedArray(
+ src_array,
+ mask=np.logical_or(
+ src_array == nodata_value,
+ np.logical_not(rv_array)
+ )
+ )
+
+ feature_stats = {
+ 'sum': float(masked.sum())}
+
+ stats.append(feature_stats)
+
+ rvds = None
+ mem_ds = None
+ feat = vlyr.GetNextFeature()
+
+ vds = None
+ rds = None
+ return stats
+
+ def vector_to_raster(self,vector_fn,in_fn,raster_fn,data_type, nodata=None):
+ '''
+ Create residual raster image from vector file
+ :param vector_fn: vector datasource
+ :param in_fn: datasource to copy projection and geotransform from
+ :param raster_fn:path to create raster file
+ :param data_type:output data type
+ :param nodata:optional NoData value
+ :return
+ '''
+ # Open the vector data source
+ source_ds = ogr.Open(vector_fn)
+ source_layer = source_ds.GetLayer()
+
+ in_ds = gdal.Open(in_fn)
+ output_format = raster_fn.split('.')[-1].upper()
+ if (output_format == 'TIF'):
+ output_format = 'GTIFF'
+ elif (output_format == 'RST'):
+ output_format = 'rst'
+ driver = gdal.GetDriverByName(output_format)
+ out_ds = driver.Create(raster_fn, in_ds.RasterXSize, in_ds.RasterYSize, 1, data_type, options=["BigTIFF=YES"])
+ out_band = out_ds.GetRasterBand(1)
+ out_ds.SetGeoTransform(in_ds.GetGeoTransform())
+
+ out_ds.SetProjection(in_ds.GetProjection().encode('utf-8', 'backslashreplace').decode('utf-8'))
+
+ if nodata is not None:
+ out_band.SetNoDataValue(nodata)
+ # Rasterize
+ gdal.RasterizeLayer(out_ds, [1], source_layer, options=["ATTRIBUTE=Residuals"])
+
+ # Cleanup
+ out_band.FlushCache()
+ out_ds.FlushCache()
+ return
+
+ def remove_edge_cells(self, full_voronoi_grid: GeoDataFrame, area_mask: GeoDataFrame,
+ area_percentile_threshold: float) -> GeoDataFrame:
+ '''
+ Ensure thiessen polygon cells retain percentile threshold of maximum size after intersection with mask of the jurisdiction
+ :param full_voronoi_grid: thiessen polygon dataframe
+ :param area_mask: mask of the jurisdiction
+ :param area_percentile_threshold: area percentile threshold
+ :return thiessen_gdf: result dataframe
+ '''
+ thiessen_gdf = gpd.overlay(full_voronoi_grid, area_mask, how="intersection", keep_geom_type=False)
+ # get area of each polygon
+ thiessen_gdf["area"] = thiessen_gdf.area
+
+ max_area = thiessen_gdf["area"].max()
+ # using the area, calculate size of cell compared to max
+ thiessen_gdf["percentcell"] = thiessen_gdf["area"] / max_area
+
+ # select cells with more than thresh% of their area within the mask
+ thiessen_gdf = thiessen_gdf[thiessen_gdf["percentcell"] > area_percentile_threshold]
+
+ return thiessen_gdf
+
+ def create_thiessen_polygon (self, grid_area, mask, density, deforestation, out_fn, raster_fn):
+ '''
+ Create thiessen polygon
+ :param grid_area: assessment grid cell area or 100,000 (ha)
+ :param mask: mask of the jurisdiction (binary map)
+ :param density: adjusted prediction density map
+ :param deforestation:Deforestation Map during the HRP
+ :param csv_name:Name of performance chart
+ :return clipped_gdf: thiessen polygon dataframe
+ '''
+ ## Create sample points:
+ # Open the Polygonized_Mask shapefile
+ mask_df = gpd.GeoDataFrame.from_file('POLYGONIZED_MASK.shp')
+
+ # Calculate grid size
+ in_ds = gdal.Open(mask)
+ grid_size = int(np.sqrt(grid_area * 10000)) // int(in_ds.GetGeoTransform()[1])
+
+ # Systematic Sampling
+ sample_points = []
+ for y in range(-1 * grid_size, in_ds.RasterYSize + 1 * grid_size, grid_size):
+ for x in range(-1 * grid_size, in_ds.RasterXSize + 1 * grid_size, grid_size):
+ # Convert raster coordinates to geographic coordinates
+ geo_x = in_ds.GetGeoTransform()[0] + x * in_ds.GetGeoTransform()[1]
+ geo_y = in_ds.GetGeoTransform()[3] + y * in_ds.GetGeoTransform()[5]
+ sample_points.append((geo_x, geo_y))
+
+ # Convert sample_points list to DataFrame
+ df = pd.DataFrame(sample_points, columns=['geo_x', 'geo_y'])
+ df['coords'] = list(zip(df['geo_x'], df['geo_y']))
+ df['coords_P'] = df['coords'].apply(Point)
+ points_df = gpd.GeoDataFrame(df, geometry='coords_P', crs=mask_df.crs)
+
+ # Convert the 'coords' column to a numpy array
+ coords = np.array(points_df['coords'].tolist())
+
+ ## Create thiessen polygon
+ vor = Voronoi(points=coords)
+
+ # Polygonize the line ridge is not infinity
+ lines = [shapely.geometry.LineString(vor.vertices[line]) for line in
+ vor.ridge_vertices if -1 not in line]
+
+ polys = shapely.ops.polygonize(lines)
+
+ # Convert Voronoi polygons (polys) into a GeoDataFrame.
+ voronois = gpd.GeoDataFrame(geometry=gpd.GeoSeries(polys), crs=mask_df.crs)
+ self.progress_updated.emit(30)
+
+ # Ensure Thiessen Polygon cells retain 99.9% of maximum size after intersection with study area
+ thiessen_gdf = self.remove_edge_cells(voronois, mask_df, 0.999)
+
+ self.progress_updated.emit(40)
+
+ # Extract polygons and multipolygons from the entire thiessen_gdf (including GeometryCollections)
+ extracted_gdf = thiessen_gdf['geometry'].apply(
+ lambda geom: [g for g in geom.geoms if
+ g.geom_type in ['Polygon', 'MultiPolygon']] if geom.geom_type == 'GeometryCollection' else [
+ geom]
+ ).explode().reset_index(drop=True)
+
+ clipped_gdf = gpd.GeoDataFrame({'geometry': extracted_gdf}, crs=thiessen_gdf.crs)
+
+ # Calculate area in hectares
+ clipped_gdf['Area_ha'] = clipped_gdf['geometry'].area / 10000
+
+ ## Calculate zonal statistics
+
+ ## Convert clipped_gdf to shapefile
+ vector_temp_path = "temp_vector.shp"
+ clipped_gdf.to_file(vector_temp_path)
+
+ # Actual Deforestation(ha)
+ stats = self.zonal_stats(vector_temp_path, deforestation, nodata_value=0)
+
+ self.progress_updated.emit(50)
+
+ # Calculate areal_resolution_of_map_pixels
+ in_ds4 = gdal.Open(density)
+ P1 = in_ds4.GetGeoTransform()[1]
+ P2 = abs(in_ds4.GetGeoTransform()[5])
+ areal_resolution_of_map_pixels = P1 * P2 / 10000
+
+ # Add the results back to the GeoDataFrame
+ clipped_gdf['Actual Deforestation(ha)'] = [(item['sum'] if item['sum'] is not None else 0) * areal_resolution_of_map_pixels for item in stats]
+ self.progress_updated.emit(60)
+
+ # Predicted Deforestation(ha)
+ stats1 = self.zonal_stats(vector_temp_path, density, nodata_value=0)
+ self.progress_updated.emit(70)
+
+ clipped_gdf['Predicted Deforestation(ha)'] = [(item['sum'] if item['sum'] is not None else 0) for item in stats1]
+
+ # ID
+ clipped_gdf['ID'] = range(1, len(clipped_gdf) + 1)
+
+ # Replace NaN or blank values with '0'
+ columns_to_fill = ['Actual Deforestation(ha)', 'Predicted Deforestation(ha)']
+ for column in columns_to_fill:
+ clipped_gdf[column] = clipped_gdf[column].fillna(0)
+
+ # Calculate residuals
+ clipped_gdf['Residuals(ha)'] = clipped_gdf['Predicted Deforestation(ha)'] - clipped_gdf['Actual Deforestation(ha)']
+
+ # Export to csv
+ csv_file_path = out_fn.split('.')[0]+'.csv'
+ clipped_gdf.drop('geometry', axis=1).to_csv(csv_file_path, columns=['ID', 'Actual Deforestation(ha)', 'Predicted Deforestation(ha)','Residuals(ha)'],
+ index=False)
+
+ # Rename columns title for shapefile
+ clipped_gdf = clipped_gdf.rename(columns={'Predicted Deforestation(ha)': 'PredDef',
+ 'Actual Deforestation(ha)': 'ActualDef',
+ 'Residuals(ha)':'Residuals'})
+
+ # Save the updated GeoDataFrame back to a shapefile
+
+ tp_file_path = out_fn.split('.')[0]+'.shp'
+ clipped_gdf.to_file(tp_file_path)
+
+ # Create residual map
+ self.vector_to_raster(tp_file_path, mask, raster_fn, gdal.GDT_Float32,-1)
+
+ return clipped_gdf
+
+ def create_deforestation_map (self, juris_mask, fmask, deforestation_cal, deforestation_cnf, out_fn_def):
+ self.progress_updated.emit(80)
+ arr_fmask = self.image_to_array(fmask)
+ arr_def_cal = self.image_to_array(deforestation_cal)
+ arr_def_cnf = self.image_to_array(deforestation_cnf)
+ arr_juris_mask = self.image_to_array(juris_mask)
+
+ deforestation_arr=np.copy(arr_fmask)
+
+ deforestation_arr[arr_def_cnf == 1] = 3
+ deforestation_arr[(arr_def_cnf == 0) & (arr_def_cal == 1)] = 2
+ deforestation_arr[(arr_def_cnf == 0) & (arr_def_cal == 0) & (arr_fmask == 1)] = 1
+ deforestation_arr[(arr_fmask == 0)] = 4 #tw 6/25/26
+ #ToDo: mask out the background
+ deforestation_arr[(arr_juris_mask == 0)] = 0 #tw 6/25/26
+ #ToDo: fix the legend
+ #
+ #write deforestation_map
+ self.array_to_image(fmask, out_fn_def, deforestation_arr, gdal.GDT_Int16, -1)
+
+ return
+
+ def create_plot(self, grid_area, clipped_gdf, title, out_fn,xmax=None, ymax=None):
+ '''
+ Create plot and save to local directory
+ :param grid_area: assessment grid cell area or 100,000 (ha)
+ :param clipped_gdf: thiessen_polygon geo-dataframe
+ :param title:plot title
+ :param out_fn: plot path
+ :param xmax: maximum x-axis value
+ :param ymax: maximum y-axis value
+ :return
+ '''
+ self.progress_updated.emit(90)
+ # Set Seaborn Style
+ sns.set()
+
+ # prepare the X/Y data
+ X = np.array(clipped_gdf['ActualDef'], dtype=np.float32)
+ Y = np.array(clipped_gdf['PredDef'], dtype=np.float32)
+ ids = np.asarray(clipped_gdf["ID"])
+
+ # Set a proportion to extend the limits
+ extension_f = 0.1
+
+ # Check if lim is string and "default"
+ if isinstance(xmax, str) and xmax.lower() == "default":
+ xmax = max(X) * (1 + extension_f)
+ else:
+ xmax = float(xmax)
+
+ if isinstance(ymax, str) and ymax.lower() == "default":
+ ymax = max(Y) * (1 + extension_f)
+ else:
+ ymax = float(ymax)
+
+ # Set a new X range from 0 to the xmax
+ X_extended = np.linspace(0, xmax, 500)
+
+ ## Perform linear regression
+ slope, intercept, _, _, _ = stats.linregress(X, Y)
+
+ # Create the equation string
+ equation = f'Y = {slope:.4f} * X + {intercept:.2f}'
+
+ # Calculate the trend line
+ trend_line = slope * X_extended + intercept
+
+ # 1-to-1 Line
+ one_to_one_line = X_extended
+
+ ## Calculate R square
+ # Get the correlation coefficient
+ r = np.corrcoef(X, Y)[0, 1]
+ # Square the correlation coefficient
+ r_squared = r ** 2
+
+ ##Calculate MedAE
+ distance_arr = [abs(X[i] - Y[i]) for i in range(len(X))]
+ MedAE = np.median(distance_arr)
+
+ ## Calculate MedAE percent
+ MedAE_percent = (MedAE / int(grid_area)) * 100
+
+ ## Calculate MAE
+ MAE=np.sum(distance_arr)/len(X)
+ MAE_percent = (MAE / int(grid_area)) * 100
+
+ ## Calculate the IoU
+ # agreement=intersection: Minimum of actual and predicted deforestation for each dot and add them all
+ agree_arr = [min(X[i],Y[i]) for i in range(len(X))]
+ agree = np.sum(agree_arr)
+
+ # union
+ union_arr = [max(X[i],Y[i]) for i in range(len(X))]
+ union = np.sum(union_arr)
+
+ # IoU
+ iou = 0 if union == 0 else agree / union * 100
+
+ ## Calculate the Difference
+ # Absolute value of predicted deforestation minus actual deforestation of each point and add them all
+ difference=np.sum(distance_arr)
+
+ # Set the figure size
+ plt.figure(figsize=(8, 6))
+
+ # Create a scatter plot
+ plt.scatter(clipped_gdf['ActualDef'], clipped_gdf['PredDef'], color='steelblue', alpha=0.5, linewidth=1.0, s=50)
+
+ # Add labels and title
+ plt.xlabel('Actual Deforestation (ha)', color='black', fontweight='bold', labelpad=10)
+ plt.ylabel('Predicted Deforestation (ha)', color='black', fontweight='bold', labelpad=10)
+ plt.title(title, color='firebrick', fontweight='bold', fontsize=20, pad=20)
+
+ # Plot the trend line
+ plt.plot(X_extended, trend_line, color='mediumseagreen', linestyle='-', label='OLS Line')
+
+ # Plot a 1-to-1 line
+ plt.plot(X_extended, one_to_one_line, color='crimson', linestyle='--',label='1:1 Line')
+
+ ## Theil-Sen Regressor
+ # Fit Theil-Sen Regressor
+ # Compute Theil-Sen estimator
+ ts_slope, ts_intercept, _, _ = stats.theilslopes(Y, X)
+
+ # Generate predictions
+ y_pred = ts_slope * X_extended + ts_intercept
+
+ # Equation of the line
+ ts_equation = f'Y = {ts_slope:.4f} * X + {ts_intercept:.2f}'
+
+ # Plot Theil-Sen Line
+ plt.plot(X_extended, y_pred, color='orange', linestyle='-', label='Theil-Sen Line')
+
+ # Add a legend in the bottom right position
+ plt.legend(loc='lower right')
+
+ plt.xlim([0, xmax])
+ plt.ylim([0, ymax])
+
+ text_x_pos = ymax * 0.05
+ text_y_start_pos = ymax * 0.9
+ text_y_gap = ymax * 0.05
+
+ # Adjust plt texts with the new calculated positions
+ plt.text(text_x_pos, text_y_start_pos, f'Theil-Sen : {ts_equation}', fontsize=11,
+ color='black')
+ plt.text(text_x_pos, text_y_start_pos - text_y_gap, f'OLS : {equation}', fontsize=11, color='black')
+ plt.text(text_x_pos, text_y_start_pos - 2 * text_y_gap, f'Samples = {len(X)}', fontsize=11, color='black')
+ plt.text(text_x_pos, text_y_start_pos - 3 * text_y_gap, f'R^2 = {r_squared:.4f}', fontsize=11, color='black')
+ plt.text(text_x_pos, text_y_start_pos - 4 * text_y_gap, f'MedAE = {MedAE:.2f} ({MedAE_percent:.2f}%)',
+ fontsize=11, color='black')
+ plt.text(text_x_pos, text_y_start_pos - 5 * text_y_gap, f'IoU = {iou:.2f}%',fontsize=11, color='black')
+
+ ## Save metrics to txt file
+ base, ext = os.path.splitext(out_fn)
+ txt_out = base + ".txt"
+ lines_to_write = [
+ f"OLS: {equation}\n",
+ f"Theil Sen: {ts_equation}\n",
+ f"Samples = {len(X)}\n",
+ f"R^2 = {r_squared:.4f}\n",
+ f"MedAE = {MedAE:.2f} ({MedAE_percent:.2f}%)\n",
+ f"MAE = {MAE:.2f} ({MAE_percent:.2f}%)\n",
+ f"IoU : {iou:.2f}%\n",
+ f"Agreement : {agree:.2f}\n",
+ f"Difference : {difference:.2f}\n",
+ ]
+
+ with open(txt_out, "w") as file:
+ file.writelines(lines_to_write)
+
+ # x, yticks
+ plt.yticks(fontsize=10, color='dimgrey')
+ plt.xticks(fontsize=10, color='dimgrey')
+
+ # Save the plot
+ plt.savefig(out_fn)
+
+ ## html
+ # Build Plotly figure
+ fig = go.Figure()
+
+ # Hover
+ fig.add_trace(go.Scatter(
+ x=X, y=Y, mode="markers",
+ name="Cells",
+ customdata=np.stack([ids, X, Y, agree_arr,distance_arr], axis=-1),
+ hovertemplate="ID: %{customdata[0]}
Actual: %{x:.2f} ha
Predicted: %{y:.2f} ha
Agreement: %{customdata[3]}
Difference: %{customdata[4]}
",
+ marker=dict(size=8, line=dict(width=0.5), color="steelblue", opacity=0.5), showlegend=False
+ ))
+
+ # OLS line trace
+ fig.add_trace(go.Scatter(
+ x=X_extended, y=trend_line, mode="lines",
+ name="OLS Line",
+ line=dict(color="mediumseagreen", dash="solid")
+ , hoverinfo="skip", hovertemplate=None
+ ))
+
+ # Theil–Sen line trace
+ fig.add_trace(go.Scatter(
+ x=X_extended, y=y_pred, mode="lines",
+ name="Theil–Sen Line",
+ line=dict(color="orange", dash="solid")
+ , hoverinfo="skip", hovertemplate=None
+ ))
+
+ # 1:1 line trace
+ fig.add_trace(go.Scatter(
+ x=X_extended, y=one_to_one_line, mode="lines",
+ name="1:1 Line",
+ line=dict(color="crimson", dash="dash")
+ , hoverinfo="skip", hovertemplate=None
+ ))
+
+ # Layout setting
+ fig.update_layout(
+ title=dict(
+ text=f"{title}",
+ font=dict(color="firebrick", size=20),
+ x=0.5, xanchor="center"
+ ),
+ xaxis=dict(
+ title=dict(text="Actual Deforestation (ha)",
+ font=dict(color="black"),
+ standoff=10),
+ tickfont=dict(size=10, color="dimgrey"),
+ range=[0, xmax],
+ constrain="domain"
+ ),
+ yaxis=dict(
+ title=dict(text="Predicted Deforestation (ha)",
+ font=dict(color="black"),
+ standoff=10),
+ tickfont=dict(size=10, color="dimgrey"),
+ range=[0, ymax]
+ ),
+ hovermode="closest",
+ legend=dict(
+ x=0.99, y=0.01, xanchor="right", yanchor="bottom", orientation="v"
+ ),
+ margin=dict(l=60, r=30, t=80, b=60),
+ paper_bgcolor= 'rgb(255,255,255)',
+ plot_bgcolor= 'rgb(234,234,242)',
+ )
+
+ # Annotations
+ stats_text = (
+ f"Theil–Sen: {ts_equation}
"
+ f"OLS: {equation}
"
+ f"Samples = {len(X)}
"
+ f"R² = {r_squared:.4f}
"
+ f"MedAE = {MedAE:.2f} ({MedAE_percent:.2f}%)
"
+ f"IoU = {iou:.2f}%
"
+ )
+
+ fig.add_annotation(
+ x=0.02, y=0.98, xref="paper", yref="paper", xanchor="left", yanchor="top",
+ text=stats_text, showarrow=False, align="left"
+ )
+
+ # Save a HTML
+ html_out = base + ".html"
+
+ pio.write_html(fig, file=html_out, include_plotlyjs=True, full_html=True)
+
+ return
+
+ def remove_temp_files(self):
+ # Files to check for and delete
+ mask_file = 'mask'
+ shapefiles_to_delete = ["TEMP_POLYGONIZED","POLYGONIZED_MASK","thiessen_polygon_temp","temp_vector"]
+
+ # Shapefile associated extensions
+ mask_file_extensions =[".tif",".rst",".rdc",".RST",".RST.aux.xml",".ref"]
+ shapefile_extensions = [".shp", ".shx", ".dbf", ".prj", ".sbn", ".sbx",".cpg", ".shp.xml"]
+
+ # Delete mask files
+ for mask_ext in mask_file_extensions:
+ mask_filename = f"{mask_file}{mask_ext}"
+ if os.path.exists(mask_filename):
+ os.remove(mask_filename)
+
+ # Delete shapefiles with associated extensions
+ for shp_base in shapefiles_to_delete:
+ for ext in shapefile_extensions:
+ full_filename = f"{shp_base}{ext}"
+ if os.path.exists(full_filename):
+ os.remove(full_filename)
+ self.progress_updated.emit(100)
+ return