diff --git a/RSCDer.py b/ECD.py similarity index 100% rename from RSCDer.py rename to ECD.py diff --git a/log.txt b/log.txt index 8e8333e..d59c2a5 100644 --- a/log.txt +++ b/log.txt @@ -1,6 +1,4 @@ -2022-05-17 16:14:17,007 - root - INFO - lic data:2022-12-01 00:00:00 -2022-05-17 16:14:17,010 - root - INFO - remain_days: 197 -2022-05-17 16:14:18,338 - root - INFO - lic data:2022-12-01 00:00:00 -2022-05-17 16:14:18,338 - root - INFO - remain_days: 197 -2022-05-17 16:35:12,026 - root - INFO - Empty module name -2022-05-17 16:35:13,397 - root - INFO - None +2022-05-23 19:52:34,008 - root - INFO - lic data:2022-12-01 00:00:00 +2022-05-23 19:52:34,012 - root - INFO - remain_days: 191 +2022-05-23 19:52:35,282 - root - INFO - lic data:2022-12-01 00:00:00 +2022-05-23 19:52:35,282 - root - INFO - remain_days: 191 diff --git a/plugins/basic_change/main.py b/plugins/basic_change/main.py index e049520..af8428b 100644 --- a/plugins/basic_change/main.py +++ b/plugins/basic_change/main.py @@ -5,7 +5,7 @@ from rscder.plugins.basic import BasicPlugin from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QIcon -from rscder.utils.project import Project, PairLayer, ResultLayer +from rscder.utils.project import BasicLayer, Project, PairLayer, ResultPointLayer, RasterLayer from rscder.gui.layercombox import LayerCombox from osgeo import gdal, gdal_array from threading import Thread @@ -77,32 +77,19 @@ class BasicMethod(BasicPlugin): basic_diff_method.triggered.connect(self.basic_diff_alg) self.message_send.connect(self.send_message) - self.result_ok.connect(self.on_result_ok) - # self.result_ok.connect(self.on_result_ok) + self.gap = 250 - - def on_data_load(self, layer_id): + def setup(self): self.basic_diff_method.setEnabled(True) - + def send_message(self, s): self.message_box.info(s) - - def on_result_ok(self, data): - layer = Project().layers[data['layer_id']] - csv_result = ResultLayer('basic_diff_result', layer, ResultLayer.POINT) - csv_result.load_file(data['csv_file']) - raster_layer = ResultLayer('basic_diff_result-raster',layer, ResultLayer.RASTER) - raster_layer.load_file(data['raster_file']) - layer.results.append(csv_result) - layer.results.append(raster_layer) - self.layer_tree.update_layer(layer.id) - - def run_basic_diff_alg(self, layer:PairLayer, out): + def run_basic_diff_alg(self, layer:PairLayer): - pth1 = layer.pth1 - pth2 = layer.pth2 + pth1 = layer.main_l1.path + pth2 = layer.main_l2.path cell_size = layer.cell_size @@ -118,7 +105,7 @@ class BasicMethod(BasicPlugin): geo = ds1.GetGeoTransform() driver = gdal.GetDriverByName('GTiff') - out_tif = os.path.join(out, 'temp.tif') + out_tif = os.path.join(Project().cmi_path, 'temp.tif') out_ds = driver.Create(out_tif, xsize, ysize, 1, gdal.GDT_Float32) out_ds.SetGeoTransform(ds1.GetGeoTransform()) out_ds.SetProjection(ds1.GetProjection()) @@ -155,7 +142,7 @@ class BasicMethod(BasicPlugin): self.message_send.emit('归一化概率中...') temp_in_ds = gdal.Open(out_tif) - out_normal_tif = os.path.join(out, '{}.tif'.format(int(np.random.rand() * 100000))) + out_normal_tif = os.path.join(Project().cmi_path, '{}-{}.tif'.format(layer.name, int(np.random.rand() * 100000))) out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte) out_normal_ds.SetGeoTransform(ds1.GetGeoTransform()) out_normal_ds.SetProjection(ds1.GetProjection()) @@ -188,7 +175,7 @@ class BasicMethod(BasicPlugin): self.message_send.emit('完成归一化概率') self.message_send.emit('计算变化表格中...') - out_csv = os.path.join(out, '{}.csv'.format(int(np.random.rand() * 100000))) + out_csv = os.path.join(Project().bcdm_path, '{}-{}.csv'.format(layer.name, int(np.random.rand() * 100000))) xblocks = xsize // cell_size[0] normal_in_ds = gdal.Open(out_normal_tif) @@ -215,11 +202,11 @@ class BasicMethod(BasicPlugin): f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255 * 100},1\n') - self.result_ok.emit({ - 'layer_id': layer.id, - 'csv_file': out_csv, - 'raster_file': out_normal_tif - }) + point_result_lalyer = ResultPointLayer(out_csv, enable=False, proj = layer.proj, geo = layer.geo) + raster_result_layer = RasterLayer(None, True, out_normal_tif, BasicLayer.BOATH_VIEW) + + layer.add_result_layer(point_result_lalyer) + layer.add_result_layer(raster_result_layer) self.message_send.emit('完成计算变化表格') @@ -237,11 +224,8 @@ class BasicMethod(BasicPlugin): if not layer.check(): return - out_dir =os.path.join(self.project.root, 'basic_diff_result') - if not os.path.exists(out_dir): - os.makedirs(out_dir, exist_ok=True) - - t = Thread(target=self.run_basic_diff_alg, args=(layer, out_dir)) + + t = Thread(target=self.run_basic_diff_alg, args=(layer,)) t.start() diff --git a/plugins/export_to/main.py b/plugins/export_to/main.py index 4dd979c..b3fbe5d 100644 --- a/plugins/export_to/main.py +++ b/plugins/export_to/main.py @@ -1,5 +1,5 @@ import shutil -from rscder.utils.project import Project, PairLayer, ResultLayer +from rscder.utils.project import Project, PairLayer, ResultPointLayer from rscder.plugins.basic import BasicPlugin from PyQt5.QtWidgets import QDialog, QHBoxLayout, QFileDialog, QComboBox, QVBoxLayout, QPushButton, QLabel, QLineEdit, QAction from PyQt5.QtGui import QIcon @@ -20,8 +20,8 @@ class ExportDialog(QDialog): result_layer_select.addItem('---', None) for layer in Project().layers.values(): - for result_layer in layer.results: - if result_layer.layer_type == ResultLayer.POINT: + for result_layer in layer.layers: + if isinstance(result_layer, ResultPointLayer): result_layer_select.addItem( layer.name[:5] + '-' + result_layer.name, result_layer) for i in range(result_layer_select.count() - 1): diff --git a/rscder/gui/layertree.py b/rscder/gui/layertree.py index 87dbdfd..63bc22f 100644 --- a/rscder/gui/layertree.py +++ b/rscder/gui/layertree.py @@ -12,7 +12,7 @@ from rscder.utils.project import PairLayer, Project class LayerTree(QtWidgets.QWidget): LAYER_TOOT = 0 - SUB_RASTER = 1 + SUB_LAYERS = 1 RESULT = 2 LEFT_RASTER = 0 RIGHT_RASTER = 1 @@ -32,195 +32,85 @@ class LayerTree(QtWidgets.QWidget): self.tree.customContextMenuRequested.connect(self.right_menu_show) self.root=QTreeWidgetItem(self.tree) self.tree.setHeaderHidden(True) + # self.tree.setHeaderLabels(['图层']) self.root.setText(0,'图层') self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png')) - - + self.tree.expandAll() self.tree.addTopLevelItem(self.root) self.tree.itemChanged.connect(self.onItemChanged) + self.tree.itemExpanded.connect(self.onItemExpanded) + + self._expand = True + self.root.setExpanded(self._expand) layout = QtWidgets.QGridLayout() layout.addWidget(self.tree) self.setLayout(layout) self.setLayoutDirection(Qt.LeftToRight) - self.is_in_add_layer = False + self.is_update_layer = False self.current_item = None + + def onItemExpanded(self, item:QtWidgets.QTreeWidgetItem): + if item == self.root: + self._expand = item.isExpanded() + return + if hasattr(item, 'item_update'): + item.item_update(item) + def onItemChanged(self, item:QtWidgets.QTreeWidgetItem, column): - if self.is_in_add_layer: + if self.is_update_layer: return if item == self.root: - return - root = item - if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT: - root = item.parent() - - layer = Project().layers[root.data(0, Qt.UserRole + 1)] - if item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: - layer.enable = item.checkState(0) == Qt.Checked - if item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER: - if item.data(0, Qt.UserRole + 1) == LayerTree.LEFT_RASTER: - layer.l1_enable = item.checkState(0) == Qt.Checked - elif item.data(0, Qt.UserRole + 1) == LayerTree.RIGHT_RASTER: - layer.l2_enable = item.checkState(0) == Qt.Checked - if item.data(0, Qt.UserRole) == LayerTree.RESULT: - layer.results[item.data(0, Qt.UserRole + 1)].enable = item.checkState(0) == Qt.Checked - - if item.data(0, Qt.UserRole) == LayerTree.GRID: - layer.grid_enable = item.checkState(0) == Qt.Checked - - self.tree_changed.emit() - - def add_layer(self, layer:str): - # self.tree.it - self.is_in_add_layer = True - layer:PairLayer = Project().layers[layer] - item_root = QtWidgets.QTreeWidgetItem(self.root) - item_root.setText(0,layer.name) - item_root.setIcon(0, QtGui.QIcon(':/icons/document.png')) - item_root.setData(0, Qt.UserRole, LayerTree.LAYER_TOOT) - item_root.setData(0, Qt.UserRole + 1, layer.id) - item_root.setCheckState(0, Qt.Checked if layer.enable else Qt.Unchecked) - - self.add_sub_layer(item_root, layer) - self.is_in_add_layer = False - - def add_sub_layer(self, item_root, layer:PairLayer): - # print(item_root.text(0)) - # print(layer.results.__len__()) - grid_item = QtWidgets.QTreeWidgetItem(item_root) - grid_item.setText(0,'格网') - grid_item.setData(0, Qt.UserRole, LayerTree.GRID) - grid_item.setCheckState(0, Qt.Checked if layer.grid_enable else Qt.Unchecked) - grid_item.setIcon(0, QtGui.QIcon(':/icons/grid.png')) - - item1 = QtWidgets.QTreeWidgetItem(item_root) - item1.setText(0, layer.l1_name) - item1.setCheckState(0, Qt.Checked if layer.l1_enable else Qt.Unchecked) - item1.setData(0, Qt.UserRole, LayerTree.SUB_RASTER) - item1.setData(0, Qt.UserRole + 1, LayerTree.LEFT_RASTER) - item1.setIcon(0, QtGui.QIcon(':/icons/layer.png')) - - item2 = QtWidgets.QTreeWidgetItem(item_root) - item2.setText(0, layer.l2_name) - item2.setCheckState(0, Qt.Checked if layer.l2_enable else Qt.Unchecked) - item2.setData(0, Qt.UserRole, LayerTree.SUB_RASTER) - item2.setData(0, Qt.UserRole + 1, LayerTree.RIGHT_RASTER) - item2.setIcon(0, QtGui.QIcon(':/icons/layer.png')) - - for ri, item in enumerate(layer.results): - item_result = QtWidgets.QTreeWidgetItem(item_root) - item_result.setText(0, item.name) - item_result.setCheckState(0, Qt.Checked if item.enable else Qt.Unchecked) - item_result.setData(0, Qt.UserRole, LayerTree.RESULT) - item_result.setData(0, Qt.UserRole + 1, ri) - - item_result.setIcon(0, QtGui.QIcon(':/icons/vector.svg')) - - self.tree.expandAll() - - def update_layer(self, layer:str): - self.is_in_add_layer = True - layer:PairLayer = Project().layers[layer] - - layer_root = None - # pdb.set_trace() - for idx in range(self.root.childCount()): - item_root = self.root.child(idx) - if item_root.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: - if item_root.data(0, Qt.UserRole + 1) == layer.id: - layer_root = item_root - break - logging.info(layer_root.text(0)) - if layer_root is None: - self.add_layer(layer.id) + self._expand = item.isExpanded() return - layer_root.setText(0,layer.name) + if hasattr(item, 'item_update'): + item.item_update(item) - while layer_root.childCount() > 0: - layer_root.removeChild(layer_root.child(0)) - - self.add_sub_layer(layer_root, layer) - self.is_in_add_layer = False + def update_layer(self): + self.is_update_layer = True + self.clear() + for layer_group in Project().layers.values(): + item_root = layer_group.get_item(self.root) + # self.root.addChild(item_root) + layer_group.grid.get_item(item_root) + for _, sub in enumerate(layer_group.layers): + sub.get_item(item_root) + # item_root.addChild(item) + self.is_update_layer = False def clear(self): self.tree.clear() self.root = QTreeWidgetItem(self.tree) - self.root.setText(0,'图层') self.tree.addTopLevelItem(self.root) - - def delete_layer(self): - item = self.current_item - if item is None: - return - if item == self.root: - return - root = item - if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT: - root = item.parent() - - if item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: - self.root.takeChild(self.root.indexOfChild(item)) - del Project().layers[root.data(0, Qt.UserRole + 1)] - elif item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER: - return - elif item.data(0, Qt.UserRole) == LayerTree.GRID: - return - elif item.data(0, Qt.UserRole) == LayerTree.RESULT: - root.takeChild(root.indexOfChild(item)) - del Project().layers[root.data(0, Qt.UserRole + 1)].results[item.data(0, Qt.UserRole + 1)] - self.update_layer(root.data(0, Qt.UserRole + 1)) - self.tree_changed.emit(root.data(0, Qt.UserRole + 1)) - - def zoom_to_layer(self): - item = self.current_item - if item is None: - return - if item == self.root: - return - root = item - if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT: - root = item.parent() - - self.zoom_to_layer_signal.emit(root.data(0, Qt.UserRole + 1)) - + self.tree.expandAll() + self.root.setText(0,'图层') + self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png')) + self.root.setExpanded(self._expand) def right_menu_show(self, position): rightMenu = QtWidgets.QMenu(self) - # QAction = QtWidgets.QAction(self.menuBar1) + item = self.tree.itemAt(position) self.current_item = item action_manager = get_action_manager() actions = [] data_load_action = action_manager.get_action('&数据加载', 'File') actions.append(data_load_action) - zoom_to_action = QtWidgets.QAction(QIcon(':/icons/full.svg'), '&缩放至该图层', self) - del_action = QtWidgets.QAction(QIcon(':/icons/delete.png'), '&删除该图层', self) - zoom_to_action.triggered.connect(self.zoom_to_layer) - del_action.triggered.connect(self.delete_layer) + if item is None: logging.info('nothing') else: if item == self.root: pass - elif item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: - actions.append(zoom_to_action) - actions.append(QtWidgets.QAction('&重命名', self)) - actions.append(del_action) - elif item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER: - actions.append(zoom_to_action) - actions.append(QtWidgets.QAction('&重命名', self)) - elif item.data(0, Qt.UserRole) == LayerTree.RESULT: - actions.append(zoom_to_action) - actions.append(QtWidgets.QAction('&重命名', self)) - actions.append(del_action) - - + else: + actions.extend(item.get_actions()) + for action in actions: rightMenu.addAction(action) diff --git a/rscder/gui/mainwindow.py b/rscder/gui/mainwindow.py index b052df6..979addc 100644 --- a/rscder/gui/mainwindow.py +++ b/rscder/gui/mainwindow.py @@ -1,6 +1,6 @@ import pdb from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QToolBox -from PyQt5.QtCore import Qt, QSize, QSettings +from PyQt5.QtCore import Qt, QSize, QSettings, pyqtSignal from PyQt5.QtGui import QIcon from PyQt5 import QtGui from PyQtAds import QtAds @@ -15,6 +15,8 @@ from rscder.utils.project import Project from rscder.gui.layercombox import LayerCombox class MainWindow(QMainWindow): + closed = pyqtSignal() + def __init__(self, parent=None, **kargs): super().__init__(parent) # self.current_instance = kargs.get('current_instance', 0) @@ -33,11 +35,8 @@ class MainWindow(QMainWindow): self.layer_tree, self.message_box, self.result_box) - self.layer_tree.tree_changed.connect(self.double_map.layer_changed) - self.layer_tree.result_clicked.connect(self.result_box.on_result) - self.result_box.on_item_click.connect(self.double_map.zoom_to_result) - self.result_box.on_item_changed.connect(Project().change_result) - self.layer_tree.zoom_to_layer_signal.connect(self.double_map.zoom_to_layer) + + self.result_box.on_item_click.connect(self.double_map.zoom_to_extent) self.action_manager = ActionManager( self.double_map, @@ -126,7 +125,7 @@ class MainWindow(QMainWindow): set_docker_fixed(self.message_dock) def closeEvent(self, event): - pass + self.closed.emit() def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: Settings.General().size = (a0.size().width(), a0.size().height()) diff --git a/rscder/gui/mapcanvas.py b/rscder/gui/mapcanvas.py index fe9f0ad..43aa9c1 100644 --- a/rscder/gui/mapcanvas.py +++ b/rscder/gui/mapcanvas.py @@ -20,7 +20,7 @@ import tempfile import cv2 import os -from rscder.utils.project import PairLayer, Project +from rscder.utils.project import BasicLayer, PairLayer, Project class DoubleCanvas(QWidget): corr_changed = pyqtSignal(str) @@ -106,18 +106,20 @@ class DoubleCanvas(QWidget): layer_list_2 = [] for layer in layers.values(): if layer.enable: - if layer.grid_enable and self.grid_show: - layer_list_1.append(layer.grid_layer.grid_layer) - layer_list_2.append(layer.grid_layer.grid_layer) - if layer.l1_enable: - layer_list_1.append(layer.l1) - if layer.l2_enable: - layer_list_2.append(layer.l2) - - for result in layer.results: - if result.enable: - layer_list_1.append(result.layer) - layer_list_2.append(result.layer) + if layer.grid.enable and self.grid_show: + layer_list_1.append(layer.grid.layer) + layer_list_2.append(layer.grid.layer) + + for sub_layer in layer.layers: + if sub_layer.enable: + if sub_layer.view_mode == BasicLayer.LEFT_VIEW: + layer_list_1.append(sub_layer.layer) + elif sub_layer.view_mode == BasicLayer.RIGHT_VIEW: + layer_list_2.append(sub_layer.layer) + elif sub_layer.view_mode == BasicLayer.BOATH_VIEW: + layer_list_2.append(sub_layer.layer) + layer_list_1.append(sub_layer.layer) + self.mapcanva1.setLayers(layer_list_1) self.mapcanva2.setLayers(layer_list_2) @@ -149,22 +151,14 @@ class DoubleCanvas(QWidget): self.mapcanva1.refresh() self.mapcanva2.refresh() - def zoom_to_result(self, xydict:dict): - x = xydict['x'] - y = xydict['y'] - if Project().current_layer is not None: - layer = Project().current_layer - else: - layer = Project().layers[list(Project().layers.keys())[0]] - - extent = QgsRectangle(x - layer.cell_size[0] * layer.xres, y - layer.cell_size[1] * layer.yres, x + layer.cell_size[0] * layer.xres, y + layer.cell_size[1] * layer.yres) + def zoom_to_extent(self, extent): + # extent = QgsRectangle(x - layer.cell_size[0] * layer.xres, y - layer.cell_size[1] * layer.yres, x + layer.cell_size[0] * layer.xres, y + layer.cell_size[1] * layer.yres) self.mapcanva1.set_extent(extent) self.mapcanva2.set_extent(extent) - def zoom_to_layer(self, layer:str): - layer:PairLayer = Project().layers[layer] - self.mapcanva1.set_extent(layer.l1.extent()) - self.mapcanva2.set_extent(layer.l2.extent()) + def zoom_to_layer(self, layer): + self.mapcanva1.set_extent(layer.extent()) + self.mapcanva2.set_extent(layer.extent()) def layer_changed(self, layer:str): self.add_layer(layer) diff --git a/rscder/gui/result.py b/rscder/gui/result.py index ebd02a4..82765fa 100644 --- a/rscder/gui/result.py +++ b/rscder/gui/result.py @@ -4,29 +4,26 @@ from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QMessageBox, QAbstractItemView, QHeaderView, QStyleFactory) - -from rscder.utils.project import PairLayer, Project, ResultLayer +from qgis.core import QgsRectangle +from rscder.utils.project import PairLayer, Project, ResultPointLayer class ResultTable(QtWidgets.QWidget): - on_item_click = pyqtSignal(dict) + on_item_click = pyqtSignal(QgsRectangle) on_item_changed = pyqtSignal(dict) def __init__(self, parent=None): super(ResultTable, self).__init__(parent) # self.tableview = QTableView(self) self.tablewidget = QTableWidget(self) - self.tablewidget.setColumnCount(4) + self.tablewidget.setColumnCount(3) self.tablewidget.setRowCount(0) - self.tablewidget.setHorizontalHeaderLabels(['X', 'Y', '概率', '变化']) + self.tablewidget.setHorizontalHeaderLabels(['变化位置(x,y)', '疑似变化概率', '目视判读']) self.tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers) - self.tablewidget.cellDoubleClicked.connect(self.onDoubleClicked) + self.tablewidget.cellClicked.connect(self.onClicked) # self.tablewidget.cellClicked.connect(self.onClicked) self.tablewidget.cellChanged.connect(self.onChanged) - - # self.tablewidget.setModel(self.tableview) - # self.tableview layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.tablewidget) self.setLayout(layout) @@ -36,27 +33,44 @@ class ResultTable(QtWidgets.QWidget): def clear(self): self.tablewidget.clear() + self.tablewidget.setColumnCount(3) + self.tablewidget.setRowCount(0) + self.tablewidget.setHorizontalHeaderLabels(['变化位置(x,y)', '疑似变化概率', '目视判读']) def onChanged(self, row, col): if self.is_in_set_data or self.no_change: return - if col == 3: + if col == 2: self.no_change = True item_idx = row item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked if item_status: self.tablewidget.item(row, col).setBackground(Qt.yellow) + self.tablewidget.item(row, col).setText('YES') else: self.tablewidget.item(row, col).setBackground(Qt.green) + self.tablewidget.item(row, col).setText('NO') # logging # logging.info() self.result.update({'row':item_idx, 'value':item_status}) self.no_change = False - def onDoubleClicked(self, row, col): - x = self.tablewidget.item(row, 0).text() - y = self.tablewidget.item(row, 1).text() - self.on_item_click.emit({'x':float(x), 'y':float(y)}) + def onClicked(self, row, col): + if col != 0: + return + data = self.result.data[row] + x = data[0] + y = data[1] + xres = self.result.layer_parent.geo[1] + yres = self.result.layer_parent.geo[5] + cell_size = self.result.layer_parent.cell_size + x_min = x - xres * cell_size[0] + x_max = x + xres * cell_size[0] + y_min = y - yres * cell_size[1] + y_max = y + yres * cell_size[1] + extent = QgsRectangle(x_min, y_min, x_max, y_max) + + self.on_item_click.emit(extent) def save(self): if self.result is None: @@ -70,26 +84,27 @@ class ResultTable(QtWidgets.QWidget): self.save() self.result = result self.clear() - self.set_data(result) - def set_data(self, data:ResultLayer): + self.show_result(result) + + def show_result(self, data:ResultPointLayer): self.is_in_set_data = True - if data.layer_type != ResultLayer.POINT: - return + self.result = data self.tablewidget.setRowCount(len(data.data)) # print(len(data.data)) self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))]) for i, d in enumerate(data.data): - self.tablewidget.setItem(i, 0, QTableWidgetItem(str(d[0]))) # X - self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[1]))) # Y - self.tablewidget.setItem(i, 2, QTableWidgetItem(str(d[2]))) # 概率 - status_item = QTableWidgetItem('变化') + self.tablewidget.setItem(i, 0, QTableWidgetItem('%.3f,%.3f'%(d[0], d[1]))) # X + self.tablewidget.setItem(i, 1, QTableWidgetItem('%.2f'%d[2])) # Y + status_item = QTableWidgetItem('') if d[3] == 0: status_item.setBackground(Qt.green) status_item.setCheckState(Qt.Unchecked) + status_item.setText('NO') elif d[3] == 1: status_item.setBackground(Qt.yellow) status_item.setCheckState(Qt.Checked) - self.tablewidget.setItem(i, 3, status_item) # 变化 + status_item.setText('YES') + self.tablewidget.setItem(i, 2, status_item) # 变化 self.tablewidget.resizeColumnsToContents() self.tablewidget.resizeRowsToContents() self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) diff --git a/rscder/plugins/basic.py b/rscder/plugins/basic.py index 905cc32..e449f68 100644 --- a/rscder/plugins/basic.py +++ b/rscder/plugins/basic.py @@ -34,7 +34,7 @@ class BasicPlugin(QObject): self.project = ctx['project'] self.mainwindow = ctx['mainwindow'] self.set_action() - self.project.layer_load.connect(self.on_data_load) + # self.project.layer_load.connect(self.on_data_load) self.project.project_init.connect(self.setup) diff --git a/rscder/utils/icons.py b/rscder/utils/icons.py new file mode 100644 index 0000000..e5e494a --- /dev/null +++ b/rscder/utils/icons.py @@ -0,0 +1,13 @@ +from PyQt5.QtGui import QIcon +from PyQt5.QtCore import QObject +from .misc import singleton + +@singleton +class IconInstance(QObject): + + def __init__(self, parent) -> None: + super().__init__(parent) + + self.GRID_ON = QIcon(':/icons/grid.png') + self.TABLE = QIcon(':/icons/table.png') + self.DELETE = QIcon(':/icons/delete.png') \ No newline at end of file diff --git a/rscder/utils/misc.py b/rscder/utils/misc.py index 9061769..7c29923 100644 --- a/rscder/utils/misc.py +++ b/rscder/utils/misc.py @@ -1,4 +1,5 @@ from functools import wraps + def singleton(cls): _instance = {} diff --git a/rscder/utils/project.py b/rscder/utils/project.py index 86dcf41..bbae413 100644 --- a/rscder/utils/project.py +++ b/rscder/utils/project.py @@ -1,16 +1,21 @@ +from cgitb import enable from collections import OrderedDict +import inspect import os from pathlib import Path +import shutil from threading import Thread from time import sleep, time from typing import Dict, List import uuid import numpy as np from osgeo import gdal, gdal_array +from rscder.utils.icons import IconInstance from rscder.utils.setting import Settings from qgis.core import QgsRasterLayer, QgsMarkerSymbol, QgsUnitTypes, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY -from PyQt5.QtCore import QObject, pyqtSignal -from PyQt5.QtGui import QColor +from PyQt5.QtCore import QObject, pyqtSignal, Qt, QThread +from PyQt5.QtWidgets import QTreeWidgetItem, QAction +from PyQt5.QtGui import QColor, QIcon, QFont import yaml from .misc import singleton @@ -20,27 +25,33 @@ def relative_path(path: str, root:str) -> str: @singleton class Project(QObject): + instance:'Project' + project_init = pyqtSignal(bool) - layer_load = pyqtSignal(str) + # layer_load = pyqtSignal() + layer_tree_update = pyqtSignal() + layer_show_update = pyqtSignal() ABSOLUTE_MODE = 'absolute' RELATIVE_MODE = 'relative' def run_auto_save(self): - + # t = QThread(self) + if self.in_save: + return + if not self.is_init: + return + if not Settings.General().auto_save: + return t = Thread(target=self.auto_save) - t.run() + t.start() def auto_save(self): # pre = time() - while True: - if Settings.General().auto_save and self.is_init: - self.save() - if self.is_closed: - break - sleep(Settings.General().auto_save_intervel) + self.in_save = True + self.save() + self.in_save = False - def __init__(self, parent=None): super().__init__(parent) @@ -53,9 +64,17 @@ class Project(QObject): self.layers:Dict[str, PairLayer] = OrderedDict() self.current_layer = None self.is_closed = False + self.in_save = False + + def set_close(): + self.is_closed = True + + parent.closed.connect(set_close) + self.run_auto_save() - def connect(self, pair_canvas, + def connect(self, + pair_canvas, layer_tree, message_box, result_table): @@ -63,21 +82,14 @@ class Project(QObject): self.layer_tree = layer_tree self.message_box = message_box self.result_table = result_table - - self.layer_load.connect(layer_tree.add_layer) - self.layer_load.connect(pair_canvas.add_layer) - - def change_result(self, layer_id, result_id, data): - if layer_id in self.layers: - result = self.layers[layer_id].results[result_id] - - if result.layer_type == ResultLayer.POINT: - result.update(data) - elif result.layer_type == ResultLayer.RASTER: - pass + IconInstance(self) + self.layer_tree_update.connect(layer_tree.update_layer) + self.layer_show_update.connect(pair_canvas.update_layer) + self.layer_tree_update.connect(self.run_auto_save) + self.layer_show_update.connect(self.run_auto_save) def setup(self, path = None, name = None): - self.is_init = True + if path is not None: self.root = path if name is None: @@ -91,8 +103,8 @@ class Project(QObject): pass else: self.load() - # self.cmi_dir = str(Path(self.root)/'cmi') - # self.project_created.emit() + + self.is_init = True self.project_init.emit(True) def save(self): @@ -101,20 +113,18 @@ class Project(QObject): 'max_memory': self.max_memory, 'max_threads': self.max_threads, 'root': self.root, - 'layers': [ layer.to_dict(None if self.file_mode == Project.ABSOLUTE_MODE else self.root) for layer in self.layers.values() ], + 'layers': [ layer.to_dict() for layer in self.layers.values() ], } - for layer in self.layers.values(): - layer.save() + # for layer in self.layers.values(): + # layer.save() with open(self.file, 'w') as f: yaml.safe_dump(data_dict, f) - # yaml.safe_dump(data_dict, open(self.file, 'w')) - def clear(self): ''' clear all layers ''' - self.layers = dict() + self.layers:Dict[str, PairLayer] = dict() self.layer_tree.clear() self.pair_canvas.clear() self.message_box.clear() @@ -133,48 +143,181 @@ class Project(QObject): self.root = data['root'] self.layers = dict() for layer in data['layers']: - player = PairLayer.from_dict(layer, None if self.file_mode == Project.ABSOLUTE_MODE else self.root) + player = PairLayer.from_dict(layer) if player.check(): self.layers[player.id] = player - self.layer_load.emit(player.id) + + self.layer_show_update.emit() + self.layer_tree_update.emit() + if len(list(self.layers.values())) > 0: + self.current_layer = list(self.layers.values())[0] + self.pair_canvas.zoom_to_layer(list(self.layers.values())[0].main_l1.layer) + except Exception as e: self.message_box.error(str(e)) self.clear() - def add_layer(self, pth1, pth2): - # self.root = str(Path(pth1).parent) - - player = PairLayer(pth1, pth2, self.cell_size) - if player.check(): - # self.layers.append(player) - self.layers[player.id] = player - self.layer_load.emit(player.id) - else: - self.message_box.error(player.msg) -class GridLayer: + def zoom_to_layer(self, data): + self.pair_canvas.zoom_to_layer(data['layer']) + + @property + def cmi_path(self): + pth = os.path.join(self.root, 'cmi') + if not os.path.exists(pth): + os.makedirs(pth) + return pth + + @property + def bcdm_path(self): + pth = os.path.join(self.root, 'bcdm') + if not os.path.exists(pth): + os.makedirs(pth) + return pth + @property + def evalution_path(self): + pth = os.path.join(self.root, 'evalution') + if not os.path.exists(pth): + os.makedirs(pth) + return pth + + @property + def other_path(self): + pth = os.path.join(self.root, 'other') + if not os.path.exists(pth): + os.makedirs(pth) + return pth + + def add_layer(self, pth1, pth2): + player = PairLayer(pth1, pth2) + if player.check(): + self.layers[player.id] = player + self.layer_show_update.emit() + self.layer_tree_update.emit() + self.pair_canvas.zoom_to_layer(player.main_l1.layer) + else: + self.message_box.error(f'{player.name} and {player.name} are not same size') + +def to_dict(obj:'BasicLayer'): + init_args = inspect.getfullargspec(obj.__class__.__init__)[0][1:] + data = {} + for args in init_args: + if hasattr(obj, args): + data[args] = getattr(obj, args) + data['type']=obj.__class__.__name__ + return data + +def from_dict(data:dict): + cls_type = data.pop('type') + if cls_type is not None and cls_type in globals(): + return globals()[cls_type](**data) + +class BasicLayer(QObject): + + LEFT_VIEW=1 + RIGHT_VIEW=2 + BOATH_VIEW=3 + + IN_MEMORY=1 + IN_FILE=2 + + layer_tree_update = pyqtSignal() + layer_show_update = pyqtSignal() + zoom_to_layer = pyqtSignal(dict) + + def __init__(self, + name='未命名', + enable = False, + icon = None, + path = None, + path_mode = IN_MEMORY, + view_mode = BOATH_VIEW,): + super().__init__(Project()) + self.enable = enable + self.name = name + self.icon = icon + self._path = path + self._expand = True + self.path_mode = path_mode + self.view_mode = view_mode + self.layer = None + self.layer_tree_update.connect(Project().layer_tree_update) + self.layer_show_update.connect(Project().layer_show_update) + self.zoom_to_layer.connect(Project().zoom_to_layer) + + @property + def path(self): + if self.path_mode == BasicLayer.IN_FILE and Project().file_mode == Project.RELATIVE_MODE: + return os.path.relpath(self._path, Project().root) + return self._path + + def get_item(self, root): + + item = QTreeWidgetItem(root) + if self.icon is not None: + item.setIcon(0, QIcon(self.icon)) + item.setText(0, self.name) + item.setCheckState(0, Qt.Checked if self.enable else Qt.Unchecked) + item.item_update = self.item_update + item.get_actions = self.get_actions + item.setExpanded(self._expand) + + return item + + def item_update(self, item:QTreeWidgetItem): + # item = self._item + # print('start update') + self.name = item.text(0) + self._expand = item.isExpanded() + pre = self.enable + cur = item.checkState(0) == Qt.Checked + if pre != cur: + self.enable = cur + self.layer_show_update.emit() + # print('end update') + + def set_layer_parent(self, layer): + self.layer_parent = layer + + def get_actions(self): + actions = [] + zoom_to_action = QAction(IconInstance().GRID_ON, '缩放至所在图层', self) + actions.append(zoom_to_action) + def zoom_to(): + self.zoom_to_layer.emit(dict(layer=self.layer)) + zoom_to_action.triggered.connect(zoom_to) + + del_action = QAction(IconInstance().DELETE, '删除图层', self) + + def del_layer(): + Project().remove_layer(self) + del_action.triggered.connect(del_layer) + actions.append(del_action) + + return actions + +class GridLayer(BasicLayer): def set_render(self): symbol_layer = QgsSimpleLineSymbolLayer() - symbol_layer.setWidth(1 * self.x_res) + symbol_layer.setWidth(1) symbol_layer.setColor(QColor.fromRgb(255,255,255, 200)) symbol = QgsLineSymbol() symbol.changeSymbolLayer(0, symbol_layer) - symbol.setWidthUnit(QgsUnitTypes.RenderMapUnits) + # symbol.setWidthUnit(QgsUnitTypes.RenderMapUnits) render = QgsSingleSymbolRenderer(symbol) - self.lines_layer.setRenderer(render) + self.layer.setRenderer(render) - def __init__(self, cell_size, ds): - self.cell_size = cell_size - self.ds = ds + def __init__(self, proj, geo, x_size, y_size, enable=True, name='格网', cell_size=(100,100), style_opts={}): - proj = ds.GetProjection() - geo = ds.GetGeoTransform() + super().__init__(name, enable, icon='') + + self.cell_size = cell_size self.proj = proj self.geo = geo - self.x_size = ds.RasterXSize - self.y_size = ds.RasterYSize + self.x_size = x_size + self.y_size = y_size self.x_min = geo[0] self.y_min = geo[3] @@ -183,19 +326,19 @@ class GridLayer: self.x_max = self.x_min + self.x_res * self.x_size self.y_max = self.y_min + self.y_res * self.y_size self.x_lines = [] - for xi in range(self.x_size // self.cell_size[0]): + for xi in range(self.x_size // self.cell_size[0] +1): self.x_lines.append(self.x_min + self.x_res * xi * self.cell_size[0]) if self.x_lines[-1] == self.x_max: self.x_lines.pop() self.x_lines.append(self.x_max) self.y_lines = [] - for yi in range(self.y_size // self.cell_size[1]): + for yi in range(self.y_size // self.cell_size[1]+1): self.y_lines.append(self.y_min + self.y_res * yi * self.cell_size[1]) if self.y_lines[-1] == self.y_max: self.y_lines.pop() self.y_lines.append(self.y_max) crs = QgsCoordinateReferenceSystem() - crs.createFromString('WKT:{}'.format(ds.GetProjection())) + crs.createFromString('WKT:{}'.format(proj)) # print(crs) lines_layer = QgsVectorLayer('LineString?crs={}'.format(crs.toProj()), 'temp-grid-outline', "memory") if not lines_layer.isValid(): @@ -214,74 +357,101 @@ class GridLayer: features.append(line) lines_layer.addFeatures(features) lines_layer.commitChanges() - self.lines_layer = lines_layer + self.layer = lines_layer self.set_render() - # self.x_lines = [ self.x_min + i * self.x_res for i in range(self.x_size) ] + + + +class RasterLayer(BasicLayer): + + def __init__(self, name=None, enable=False, path=None, view_mode=BasicLayer.BOATH_VIEW): + if name is None: + name = os.path.splitext(os.path.basename(path))[0] + super().__init__(name, enable, ':/icons/raster.png', path, BasicLayer.IN_FILE, view_mode) + self.layer = QgsRasterLayer(self.path, self.name) + + def compare(self, other:'RasterLayer'): + ds1 = gdal.Open(self.path) + ds2 = gdal.Open(other.path) + if ds1 is None or ds2 is None: + Project().message_box.error('图层打开失败') + return False + + if ds1.RasterXSize != ds2.RasterXSize or ds1.RasterYSize != ds2.RasterYSize: + Project().message_box.error('图层尺寸不一致') + return False + + return True + + @property + def geo(self): + ds = gdal.Open(self.path) + if ds is None: + return None + geo = ds.GetGeoTransform() + del ds + return geo @property - def grid_layer(self): - return self.lines_layer - - def to_dict(self): - return { - 'cell_size': self.cell_size - } + def proj(self): + ds = gdal.Open(self.path) + if ds is None: + return None + proj = ds.GetProjection() + del ds + return proj - @staticmethod - def from_dict(data): - return GridLayer() + @property + def size(self): + ds = gdal.Open(self.path) + if ds is None: + return None + s = (ds.RasterXSize, ds.RasterYSize) + del ds + return s -class ResultLayer: +class VectorLayer(BasicLayer): + pass - POINT = 0 - RASTER = 1 - def __init__(self, name, parent, layer_type = POINT): - self.layer_type = layer_type +class ResultPointLayer(BasicLayer): + + def __init__(self, path, name=None, enable = False, proj = None, geo = None): + if name is None: + name = os.path.splitext(os.path.basename(path))[0] + super().__init__(name, enable, icon=':/icons/points.png', path=path, path_mode = BasicLayer.IN_FILE, view_mode=BasicLayer.BOATH_VIEW ) self.data = None - self.layer = None - self.name = name - self.path = None - self.wkt = None - self.enable = False - self.parent = parent + self.wkt = proj + self.geo = geo + + self.load_point_file() def save(self): - if self.layer_type == ResultLayer.POINT: - with open(self.path, 'w') as f: - f.write('x,y,diff,status\n') - for i in range(len(self.data)): - f.write('{},{},{},{}\n'.format(self.data[i][0], self.data[i][1], self.data[i][2], int(self.data[i][3]))) + + with open(self.path, 'w') as f: + f.write('x,y,diff,status\n') + for i in range(len(self.data)): + f.write('{},{},{},{}\n'.format(self.data[i][0], self.data[i][1], self.data[i][2], int(self.data[i][3]))) def update(self, data): - if self.layer_type == ResultLayer.POINT: - row = data['row'] - value = data['value'] - self.data[row][-1] = value - self.update_point_layer(row) - elif self.layer_type == ResultLayer.RASTER: - pass - - def load_file(self, path): - self.path = path - if self.layer_type == ResultLayer.POINT: - self.load_point_file() - elif self.layer_type == ResultLayer.RASTER: - self.load_raster_file() - else: - raise Exception('Unknown layer type') + row = data['row'] + value = data['value'] + self.data[row][-1] = int(value) + self.update_point_layer(row) def format_point_layer(self, layer): layer.setLabelsEnabled(True) lyr = QgsPalLayerSettings() lyr.enabled = True - lyr.fieldName = 'fid' + lyr.fieldName = 'prob' lyr.placement = QgsPalLayerSettings.OverPoint - lyr.textNamedStyle = 'Medium' + lyr.xOffset = 2 + lyr.yOffset = -2 + lyr.textFont = QFont('Times New Roman', 16) text_format = QgsTextFormat() - text_format.color = QColor('#ffffff') + text_format.color = QColor.fromRgb(255,0,0) text_format.background().color = QColor('#000000') text_format.buffer().setEnabled(True) text_format.buffer().setSize(1) @@ -296,11 +466,12 @@ class ResultLayer: layer.setLabeling(rules) def set_render(self, layer): - symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 5 * self.parent.xres }) - symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderMetersInMapUnits) + # xres = self.geo[1] + symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 2 }) + symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderMillimeters) category_change = QgsRendererCategory(1, symbol_change,'change') - symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': '0'}) + symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': 0}) category_unchange = QgsRendererCategory(0, symbol_unchange, 'unchange') render = QgsCategorizedSymbolRenderer('status', [category_change, category_unchange]) @@ -320,7 +491,7 @@ class ResultLayer: else: crs = QgsCoordinateReferenceSystem() - uri = 'Point?crs={}&field=status:integer'.format(crs.toProj()) + uri = 'Point?crs={}&field=status:integer&field=prob:string'.format(crs.toProj()) layer = QgsVectorLayer(uri, self.name, "memory") if not layer.isValid(): Project().message_box.error('Failed to create layer') @@ -334,6 +505,10 @@ class ResultLayer: point.setId(i) point.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1]))) point.setAttribute('status', int(d[-1])) + if d[-1] == 0: + point.setAttribute('prob', '') + else: + point.setAttribute('prob', '%.2f'%(d[2])) # point.setAttribute('id', i) features.append(point) layer.addFeatures(features) @@ -351,152 +526,117 @@ class ResultLayer: feature = self.layer.getFeature(i+1) if feature is None: continue - feature.setAttribute('status', d[-1]) + feature.setAttribute('status', int(d[-1])) + if d[-1] == 0: + feature.setAttribute('prob', '') + else: + feature.setAttribute('prob', '%.2f'%(d[2])) else: feature = self.layer.getFeature(row+1) # print(feature) if feature is None: return - # print(feature.fields().names()) - # self.layer.deleteFeature(feature.id()) - # del feature feature.setAttribute('status', int(self.data[row][-1])) + if self.data[row][-1] == 0: + feature.setAttribute('prob', '') + else: + feature.setAttribute('prob', '%.2f'%(self.data[row][2])) self.layer.updateFeature(feature) self.layer.commitChanges() - def load_raster_file(self): - ds = gdal.Open(self.path) - if ds is None: - return - self.layer = QgsRasterLayer(self.path, self.name) + def get_actions(self): + actions = super().get_actions() + show_in_table = QAction(IconInstance().TABLE, '显示在表格中') + actions.insert(0, show_in_table) - @staticmethod - def from_dict(data, parent, root = None): - result = ResultLayer(data['name'], parent, data['layer_type']) - result.wkt = data['wkt'] - if root is not None: - result.load_file(str(Path(root) / data['path'])) - else: - result.load_file(data['path']) - return result + def show_to_table(): + Project().result_table.show_result(self) + + show_in_table.triggered.connect(show_to_table) - def to_dict(self, root=None): - return { - 'name': self.name, - 'layer_type': self.layer_type, - 'wkt': self.wkt, - 'path': self.path if root is None else str(Path(self.path).relative_to(root)) - } + return actions # def load_file(self, path): -class PairLayer: +class PairLayer(BasicLayer): - def to_dict(self, root = None): - if root is None: - return { - 'pth1': self.pth1, - 'pth2': self.pth2, - 'l1_name': self.l1_name, - 'l2_name': self.l2_name, - 'cell_size': self.cell_size, - 'results': [r.to_dict(root) for r in self.results], - 'name': self.name - - } - else: - return { - 'pth1': relative_path(self.pth1, root), - 'pth2': relative_path(self.pth2, root), - 'name': self.name, - 'l1_name': self.l1_name, - 'l2_name': self.l2_name, - 'cell_size': self.cell_size, - 'results': [r.to_dict(root) for r in self.results] - } - - @staticmethod - def from_dict(data, root = None): - if root is None: - layer = PairLayer(data['pth1'], data['pth2'], data['cell_size']) - else: - layer = PairLayer(os.path.join(root, data['pth1']), os.path.join(root, data['pth2']), data['cell_size']) - layer.l1_name = data['l1_name'] - layer.l2_name = data['l2_name'] - layer.name = data['name'] - - for r in data['results']: - layer.results.append(ResultLayer.from_dict(r, layer, root)) - # layer.grid_layer = GridLayer.from_dict(data['grid_layer']) - return layer - - def save(self): - for r in self.results: - r.save() - - def __init__(self, pth1, pth2, cell_size) -> None: - self.pth1 = pth1 - self.pth2 = pth2 - self.enable = True - self.l1_enable = True - self.l2_enable = True - self.grid_enable = True + def __init__(self, pth1, pth2) -> None: + + self.layers:List[BasicLayer] = [] self.id = str(uuid.uuid1()) - self.name = '{}-{}'.format(os.path.basename(pth1), os.path.basename(pth2)) - self.l1_name = os.path.basename(pth1) - self.l2_name = os.path.basename(pth2) - - self.l1_pres = [] # 预处理数据 - self.l2_pres = [] - - self.cell_size = cell_size - self.msg = '' self.checked = False + self.main_l1 = RasterLayer(path = pth1, enable=True, view_mode=BasicLayer.LEFT_VIEW) + self.main_l2 = RasterLayer(path = pth2, enable=True, view_mode=BasicLayer.RIGHT_VIEW) + self.main_l1.set_layer_parent(self) + self.main_l2.set_layer_parent(self) + self.grid = None + self.cell_size = Project().cell_size + name = os.path.basename(pth1)[:4] + '-' + os.path.basename(pth2)[:4] + # self.layer_update.connect(Project().layer_updated) - self.xsize = 0 - self.ysize = 0 - self.xres = 0 - self.yres = 0 - - self.wkt = None - - self.results:List[ResultLayer] = [] - + super().__init__(name, True, ':/icons/document.png') + self.layer = self.main_l1.layer + if self.check(): + self.geo = self.main_l1.geo + self.proj = self.main_l1.proj + self.size = self.main_l1.size + self.layers.append(self.main_l1) + self.layers.append(self.main_l2) + self.grid = GridLayer(self.proj, self.geo, self.size[0], self.size[1], cell_size=Project().cell_size) + self.grid.set_layer_parent(self) + # self.layers.append(self.grid) + def check(self): if self.checked: return self.checked - if not os.path.exists(self.pth1): - self.msg = '图层1不存在' - return False - if not os.path.exists(self.pth2): - self.msg = '图层2不存在' - return False + self.checked = self.main_l1.compare(self.main_l2) + return True + + def add_result_layer(self, result): + result.set_layer_parent(self) + self.layers.insert(0, result) + self.layer_show_update.emit() + self.layer_tree_update.emit() + + def has_layer(self, layer): + for ilayer in self.layers: + if ilayer is layer: + return True + return False + + def remove_layer(self, layer): + idx = -1 + for ilayer in self.layers: + idx += 1 + if ilayer is layer: + break + if idx >= len(self.layers): + return - ds1 = gdal.Open(self.pth1) - ds2 = gdal.Open(self.pth2) - if ds1 is None or ds2 is None: - self.msg = '图层打开失败' - return False + if layer is self.grid or layer is self.main_l1 or layer is self.main_l2: + return - if ds1.RasterXSize != ds2.RasterXSize or ds1.RasterYSize != ds2.RasterYSize: - self.msg = '图层尺寸不一致' - return False + self.layers.pop(idx) + del layer - self.xsize = ds1.RasterXSize - self.ysize = ds1.RasterYSize + def to_dict(self): + data=dict( + name=self.name, + enable=self.enable, + pth1=self.main_l1.path, + pth2=self.main_l2.path, + layers=[to_dict(l) for l in self.layers if not (l is self.grid or l is self.main_l1 or l is self.main_l2) ] + ) + return data + - self.xres = ds1.GetGeoTransform()[1] - self.yres = ds1.GetGeoTransform()[5] - - self.wkt = ds1.GetProjection() - - self.grid_layer = GridLayer(self.cell_size, ds1) - - del ds1 - del ds2 - - self.l1 = QgsRasterLayer(self.pth1, self.l1_name) - self.l2 = QgsRasterLayer(self.pth2, self.l2_name) - self.checked = True - return True \ No newline at end of file + @staticmethod + def from_dict(data): + player = PairLayer(data['pth1'], data['pth2']) + player.name = data['name'] + for layer in data['layers']: + l = from_dict(layer) + l.set_layer_parent(player) + player.layers.insert(0,l) + return player \ No newline at end of file diff --git a/rscder/utils/setting.py b/rscder/utils/setting.py index 8ecd3d4..9eca375 100644 --- a/rscder/utils/setting.py +++ b/rscder/utils/setting.py @@ -151,7 +151,7 @@ class Settings(QSettings): with Settings(Settings.General.PRE) as s: return s.value('auto_save', True) - @property.setter + @auto_save.setter def auto_save(self, value): if isinstance(value, bool): pass @@ -168,7 +168,7 @@ class Settings(QSettings): with Settings(Settings.General.PRE) as s: return s.value('auto_save_intervel', 30) - @property.setter + @auto_save_intervel.setter def auto_save_intervel(self, value): if isinstance(value, int) and value > 0: pass