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