完成联动
This commit is contained in:
parent
0843a369be
commit
da8d1f178e
@ -2,16 +2,60 @@ import math
|
|||||||
import os
|
import os
|
||||||
import pdb
|
import pdb
|
||||||
from rscder.plugins.basic import BasicPlugin
|
from rscder.plugins.basic import BasicPlugin
|
||||||
from PyQt5.QtWidgets import QAction
|
from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton
|
||||||
from PyQt5.QtCore import pyqtSignal
|
from PyQt5.QtCore import pyqtSignal
|
||||||
from rscder.utils.project import PairLayer, ResultLayer
|
from PyQt5.QtGui import QIcon
|
||||||
|
from rscder.utils.project import Project, PairLayer, ResultLayer
|
||||||
|
from rscder.gui.layercombox import LayerCombox
|
||||||
from osgeo import gdal, gdal_array
|
from osgeo import gdal, gdal_array
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
class MyDialog(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setWindowTitle('BasicChange')
|
||||||
|
self.setWindowIcon(QIcon(":/icons/logo.svg"))
|
||||||
|
|
||||||
|
self.setFixedWidth(500)
|
||||||
|
|
||||||
|
self.layer_select = LayerCombox(self)
|
||||||
|
self.layer_select.setFixedWidth(400)
|
||||||
|
|
||||||
|
# self.number_input = QLineEdit(self)
|
||||||
|
|
||||||
|
self.ok_button = QPushButton('OK', self)
|
||||||
|
self.ok_button.setIcon(QIcon(":/icons/ok.svg"))
|
||||||
|
self.ok_button.clicked.connect(self.on_ok)
|
||||||
|
|
||||||
|
self.cancel_button = QPushButton('Cancel', self)
|
||||||
|
self.cancel_button.setIcon(QIcon(":/icons/cancel.svg"))
|
||||||
|
self.cancel_button.clicked.connect(self.on_cancel)
|
||||||
|
|
||||||
|
self.button_layout = QHBoxLayout()
|
||||||
|
self.button_layout.addWidget(self.ok_button)
|
||||||
|
self.button_layout.addWidget(self.cancel_button)
|
||||||
|
|
||||||
|
self.main_layout = QVBoxLayout()
|
||||||
|
self.main_layout.addWidget(self.layer_select)
|
||||||
|
# self.main_layout.addWidget(self.number_input)
|
||||||
|
self.main_layout.addLayout(self.button_layout)
|
||||||
|
self.setLayout(self.main_layout)
|
||||||
|
|
||||||
|
def on_ok(self):
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def on_cancel(self):
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BasicMethod(BasicPlugin):
|
class BasicMethod(BasicPlugin):
|
||||||
|
|
||||||
message_send = pyqtSignal(str)
|
message_send = pyqtSignal(str)
|
||||||
table_result_ok = pyqtSignal(str)
|
result_ok = pyqtSignal(dict)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def info():
|
def info():
|
||||||
@ -33,34 +77,31 @@ class BasicMethod(BasicPlugin):
|
|||||||
basic_diff_method.triggered.connect(self.basic_diff_alg)
|
basic_diff_method.triggered.connect(self.basic_diff_alg)
|
||||||
|
|
||||||
self.message_send.connect(self.send_message)
|
self.message_send.connect(self.send_message)
|
||||||
self.table_result_ok.connect(self.on_table_result_ok)
|
self.result_ok.connect(self.on_result_ok)
|
||||||
|
# self.result_ok.connect(self.on_result_ok)
|
||||||
self.gap = 128
|
self.gap = 128
|
||||||
|
|
||||||
|
|
||||||
def on_data_load(self, layer_id):
|
def on_data_load(self, layer_id):
|
||||||
self.basic_diff_method.setEnabled(True)
|
self.basic_diff_method.setEnabled(True)
|
||||||
|
|
||||||
def send_message(self, s):
|
def send_message(self, s):
|
||||||
self.message_box.info(s)
|
self.message_box.info(s)
|
||||||
|
|
||||||
def on_table_result_ok(self, s):
|
def on_result_ok(self, data):
|
||||||
with open(s, 'r') as f:
|
layer = Project().layers[data['layer_id']]
|
||||||
lines = f.readlines()
|
csv_result = ResultLayer('basic_diff_result', ResultLayer.POINT)
|
||||||
data_lines = lines[1:]
|
csv_result.load_file(data['csv_file'])
|
||||||
|
layer.results.append(csv_result)
|
||||||
|
self.layer_tree.update_layer(layer.id)
|
||||||
|
|
||||||
if len(data_lines) > 0:
|
def run_basic_diff_alg(self, layer:PairLayer, out):
|
||||||
data_table = []
|
|
||||||
for l in data_lines:
|
|
||||||
l = l.strip()
|
|
||||||
ls = l.split(',')
|
|
||||||
ls = [float(i) for i in ls]
|
|
||||||
data_table.append(ls)
|
|
||||||
result = ResultLayer(ResultLayer.POINT)
|
|
||||||
result.data = data_table
|
|
||||||
self.result_table.set_data(result)
|
|
||||||
|
|
||||||
|
pth1 = layer.pth1
|
||||||
|
pth2 = layer.pth2
|
||||||
|
|
||||||
|
cell_size = layer.cell_size
|
||||||
|
|
||||||
def run_basic_diff_alg(self, pth1, pth2, cell_size, out):
|
|
||||||
self.message_send.emit('开始计算差分法')
|
self.message_send.emit('开始计算差分法')
|
||||||
|
|
||||||
ds1 = gdal.Open(pth1)
|
ds1 = gdal.Open(pth1)
|
||||||
@ -125,7 +166,7 @@ class BasicMethod(BasicPlugin):
|
|||||||
self.message_send.emit('完成归一化概率')
|
self.message_send.emit('完成归一化概率')
|
||||||
|
|
||||||
self.message_send.emit('计算变化表格中...')
|
self.message_send.emit('计算变化表格中...')
|
||||||
out_csv = os.path.join(out, 'diff_table.csv')
|
out_csv = os.path.join(out, '{}.csv'.format(int(np.random.rand() * 100000)))
|
||||||
xblocks = xsize // cell_size[0]
|
xblocks = xsize // cell_size[0]
|
||||||
|
|
||||||
normal_in_ds = gdal.Open(out_normal_tif)
|
normal_in_ds = gdal.Open(out_normal_tif)
|
||||||
@ -151,7 +192,11 @@ class BasicMethod(BasicPlugin):
|
|||||||
center_y = center_y * geo[5] + geo [3]
|
center_y = center_y * geo[5] + geo [3]
|
||||||
f.write(f'{center_x},{center_y},{block_data_xy.mean()},1\n')
|
f.write(f'{center_x},{center_y},{block_data_xy.mean()},1\n')
|
||||||
|
|
||||||
self.table_result_ok.emit(out_csv)
|
|
||||||
|
self.result_ok.emit({
|
||||||
|
'layer_id': layer.id,
|
||||||
|
'csv_file': out_csv,
|
||||||
|
})
|
||||||
|
|
||||||
self.message_send.emit('完成计算变化表格')
|
self.message_send.emit('完成计算变化表格')
|
||||||
|
|
||||||
@ -159,10 +204,13 @@ class BasicMethod(BasicPlugin):
|
|||||||
|
|
||||||
def basic_diff_alg(self):
|
def basic_diff_alg(self):
|
||||||
# layer_select =
|
# layer_select =
|
||||||
layer:PairLayer = list(self.project.layers.values())[0]
|
layer = None
|
||||||
|
layer_select = MyDialog(self.mainwindow)
|
||||||
img1 = layer.pth1
|
if(layer_select.exec_()):
|
||||||
img2 = layer.pth2
|
layer = layer_select.layer_select.current_layer
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
# layer:PairLayer = list(self.project.layers.values())[0]
|
||||||
|
|
||||||
if not layer.check():
|
if not layer.check():
|
||||||
return
|
return
|
||||||
@ -170,7 +218,7 @@ class BasicMethod(BasicPlugin):
|
|||||||
if not os.path.exists(out_dir):
|
if not os.path.exists(out_dir):
|
||||||
os.makedirs(out_dir, exist_ok=True)
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
|
||||||
t = Thread(target=self.run_basic_diff_alg, args=(img1, img2, layer.cell_size, out_dir))
|
t = Thread(target=self.run_basic_diff_alg, args=(layer, out_dir))
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
|
||||||
|
247
pyinstaller.help
247
pyinstaller.help
@ -1,247 +0,0 @@
|
|||||||
usage: pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME]
|
|
||||||
[--add-data <SRC;DEST or SRC:DEST>]
|
|
||||||
[--add-binary <SRC;DEST or SRC:DEST>] [-p DIR]
|
|
||||||
[--hidden-import MODULENAME]
|
|
||||||
[--collect-submodules MODULENAME]
|
|
||||||
[--collect-data MODULENAME] [--collect-binaries MODULENAME]
|
|
||||||
[--collect-all MODULENAME] [--copy-metadata PACKAGENAME]
|
|
||||||
[--recursive-copy-metadata PACKAGENAME]
|
|
||||||
[--additional-hooks-dir HOOKSPATH]
|
|
||||||
[--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES]
|
|
||||||
[--key KEY] [--splash IMAGE_FILE]
|
|
||||||
[-d {all,imports,bootloader,noarchive}] [-s] [--noupx]
|
|
||||||
[--upx-exclude FILE] [-c] [-w]
|
|
||||||
[-i <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">]
|
|
||||||
[--disable-windowed-traceback] [--version-file FILE]
|
|
||||||
[-m <FILE or XML>] [-r RESOURCE] [--uac-admin]
|
|
||||||
[--uac-uiaccess] [--win-private-assemblies]
|
|
||||||
[--win-no-prefer-redirects]
|
|
||||||
[--osx-bundle-identifier BUNDLE_IDENTIFIER]
|
|
||||||
[--target-architecture ARCH] [--codesign-identity IDENTITY]
|
|
||||||
[--osx-entitlements-file FILENAME] [--runtime-tmpdir PATH]
|
|
||||||
[--bootloader-ignore-signals] [--distpath DIR]
|
|
||||||
[--workpath WORKPATH] [-y] [--upx-dir UPX_DIR] [-a]
|
|
||||||
[--clean] [--log-level LEVEL]
|
|
||||||
scriptname [scriptname ...]
|
|
||||||
|
|
||||||
positional arguments:
|
|
||||||
scriptname name of scriptfiles to be processed or exactly one
|
|
||||||
.spec-file. If a .spec-file is specified, most options
|
|
||||||
are unnecessary and are ignored.
|
|
||||||
|
|
||||||
optional arguments:
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
-v, --version Show program version info and exit.
|
|
||||||
--distpath DIR Where to put the bundled app (default: .\dist)
|
|
||||||
--workpath WORKPATH Where to put all the temporary work files, .log, .pyz
|
|
||||||
and etc. (default: .\build)
|
|
||||||
-y, --noconfirm Replace output directory (default:
|
|
||||||
SPECPATH\dist\SPECNAME) without asking for
|
|
||||||
confirmation
|
|
||||||
--upx-dir UPX_DIR Path to UPX utility (default: search the execution
|
|
||||||
path)
|
|
||||||
-a, --ascii Do not include unicode encoding support (default:
|
|
||||||
included if available)
|
|
||||||
--clean Clean PyInstaller cache and remove temporary files
|
|
||||||
before building.
|
|
||||||
--log-level LEVEL Amount of detail in build-time console messages. LEVEL
|
|
||||||
may be one of TRACE, DEBUG, INFO, WARN, ERROR,
|
|
||||||
CRITICAL (default: INFO).
|
|
||||||
|
|
||||||
What to generate:
|
|
||||||
-D, --onedir Create a one-folder bundle containing an executable
|
|
||||||
(default)
|
|
||||||
-F, --onefile Create a one-file bundled executable.
|
|
||||||
--specpath DIR Folder to store the generated spec file (default:
|
|
||||||
current directory)
|
|
||||||
-n NAME, --name NAME Name to assign to the bundled app and spec file
|
|
||||||
(default: first script's basename)
|
|
||||||
|
|
||||||
What to bundle, where to search:
|
|
||||||
--add-data <SRC;DEST or SRC:DEST>
|
|
||||||
Additional non-binary files or folders to be added to
|
|
||||||
the executable. The path separator is platform
|
|
||||||
specific, ``os.pathsep`` (which is ``;`` on Windows
|
|
||||||
and ``:`` on most unix systems) is used. This option
|
|
||||||
can be used multiple times.
|
|
||||||
--add-binary <SRC;DEST or SRC:DEST>
|
|
||||||
Additional binary files to be added to the executable.
|
|
||||||
See the ``--add-data`` option for more details. This
|
|
||||||
option can be used multiple times.
|
|
||||||
-p DIR, --paths DIR A path to search for imports (like using PYTHONPATH).
|
|
||||||
Multiple paths are allowed, separated by ``';'``, or
|
|
||||||
use this option multiple times. Equivalent to
|
|
||||||
supplying the ``pathex`` argument in the spec file.
|
|
||||||
--hidden-import MODULENAME, --hiddenimport MODULENAME
|
|
||||||
Name an import not visible in the code of the
|
|
||||||
script(s). This option can be used multiple times.
|
|
||||||
--collect-submodules MODULENAME
|
|
||||||
Collect all submodules from the specified package or
|
|
||||||
module. This option can be used multiple times.
|
|
||||||
--collect-data MODULENAME, --collect-datas MODULENAME
|
|
||||||
Collect all data from the specified package or module.
|
|
||||||
This option can be used multiple times.
|
|
||||||
--collect-binaries MODULENAME
|
|
||||||
Collect all binaries from the specified package or
|
|
||||||
module. This option can be used multiple times.
|
|
||||||
--collect-all MODULENAME
|
|
||||||
Collect all submodules, data files, and binaries from
|
|
||||||
the specified package or module. This option can be
|
|
||||||
used multiple times.
|
|
||||||
--copy-metadata PACKAGENAME
|
|
||||||
Copy metadata for the specified package. This option
|
|
||||||
can be used multiple times.
|
|
||||||
--recursive-copy-metadata PACKAGENAME
|
|
||||||
Copy metadata for the specified package and all its
|
|
||||||
dependencies. This option can be used multiple times.
|
|
||||||
--additional-hooks-dir HOOKSPATH
|
|
||||||
An additional path to search for hooks. This option
|
|
||||||
can be used multiple times.
|
|
||||||
--runtime-hook RUNTIME_HOOKS
|
|
||||||
Path to a custom runtime hook file. A runtime hook is
|
|
||||||
code that is bundled with the executable and is
|
|
||||||
executed before any other code or module to set up
|
|
||||||
special features of the runtime environment. This
|
|
||||||
option can be used multiple times.
|
|
||||||
--exclude-module EXCLUDES
|
|
||||||
Optional module or package (the Python name, not the
|
|
||||||
path name) that will be ignored (as though it was not
|
|
||||||
found). This option can be used multiple times.
|
|
||||||
--key KEY The key used to encrypt Python bytecode.
|
|
||||||
--splash IMAGE_FILE (EXPERIMENTAL) Add an splash screen with the image
|
|
||||||
IMAGE_FILE to the application. The splash screen can
|
|
||||||
show progress updates while unpacking.
|
|
||||||
|
|
||||||
How to generate:
|
|
||||||
-d {all,imports,bootloader,noarchive}, --debug {all,imports,bootloader,noarchive}
|
|
||||||
Provide assistance with debugging a frozen
|
|
||||||
application. This argument may be provided multiple
|
|
||||||
times to select several of the following options.
|
|
||||||
|
|
||||||
- all: All three of the following options.
|
|
||||||
|
|
||||||
- imports: specify the -v option to the underlying
|
|
||||||
Python interpreter, causing it to print a message
|
|
||||||
each time a module is initialized, showing the
|
|
||||||
place (filename or built-in module) from which it
|
|
||||||
is loaded. See
|
|
||||||
https://docs.python.org/3/using/cmdline.html#id4.
|
|
||||||
|
|
||||||
- bootloader: tell the bootloader to issue progress
|
|
||||||
messages while initializing and starting the
|
|
||||||
bundled app. Used to diagnose problems with
|
|
||||||
missing imports.
|
|
||||||
|
|
||||||
- noarchive: instead of storing all frozen Python
|
|
||||||
source files as an archive inside the resulting
|
|
||||||
executable, store them as files in the resulting
|
|
||||||
output directory.
|
|
||||||
|
|
||||||
-s, --strip Apply a symbol-table strip to the executable and
|
|
||||||
shared libs (not recommended for Windows)
|
|
||||||
--noupx Do not use UPX even if it is available (works
|
|
||||||
differently between Windows and *nix)
|
|
||||||
--upx-exclude FILE Prevent a binary from being compressed when using upx.
|
|
||||||
This is typically used if upx corrupts certain
|
|
||||||
binaries during compression. FILE is the filename of
|
|
||||||
the binary without path. This option can be used
|
|
||||||
multiple times.
|
|
||||||
|
|
||||||
Windows and Mac OS X specific options:
|
|
||||||
-c, --console, --nowindowed
|
|
||||||
Open a console window for standard i/o (default). On
|
|
||||||
Windows this option will have no effect if the first
|
|
||||||
script is a '.pyw' file.
|
|
||||||
-w, --windowed, --noconsole
|
|
||||||
Windows and Mac OS X: do not provide a console window
|
|
||||||
for standard i/o. On Mac OS X this also triggers
|
|
||||||
building an OS X .app bundle. On Windows this option
|
|
||||||
will be set if the first script is a '.pyw' file. This
|
|
||||||
option is ignored in *NIX systems.
|
|
||||||
-i <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">, --icon <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">
|
|
||||||
FILE.ico: apply that icon to a Windows executable.
|
|
||||||
FILE.exe,ID, extract the icon with ID from an exe.
|
|
||||||
FILE.icns: apply the icon to the .app bundle on Mac OS
|
|
||||||
X. Use "NONE" to not apply any icon, thereby making
|
|
||||||
the OS to show some default (default: apply
|
|
||||||
PyInstaller's icon)
|
|
||||||
--disable-windowed-traceback
|
|
||||||
Disable traceback dump of unhandled exception in
|
|
||||||
windowed (noconsole) mode (Windows and macOS only),
|
|
||||||
and instead display a message that this feature is
|
|
||||||
disabled.
|
|
||||||
|
|
||||||
Windows specific options:
|
|
||||||
--version-file FILE add a version resource from FILE to the exe
|
|
||||||
-m <FILE or XML>, --manifest <FILE or XML>
|
|
||||||
add manifest FILE or XML to the exe
|
|
||||||
-r RESOURCE, --resource RESOURCE
|
|
||||||
Add or update a resource to a Windows executable. The
|
|
||||||
RESOURCE is one to four items,
|
|
||||||
FILE[,TYPE[,NAME[,LANGUAGE]]]. FILE can be a data file
|
|
||||||
or an exe/dll. For data files, at least TYPE and NAME
|
|
||||||
must be specified. LANGUAGE defaults to 0 or may be
|
|
||||||
specified as wildcard * to update all resources of the
|
|
||||||
given TYPE and NAME. For exe/dll files, all resources
|
|
||||||
from FILE will be added/updated to the final
|
|
||||||
executable if TYPE, NAME and LANGUAGE are omitted or
|
|
||||||
specified as wildcard *.This option can be used
|
|
||||||
multiple times.
|
|
||||||
--uac-admin Using this option creates a Manifest which will
|
|
||||||
request elevation upon application restart.
|
|
||||||
--uac-uiaccess Using this option allows an elevated application to
|
|
||||||
work with Remote Desktop.
|
|
||||||
|
|
||||||
Windows Side-by-side Assembly searching options (advanced):
|
|
||||||
--win-private-assemblies
|
|
||||||
Any Shared Assemblies bundled into the application
|
|
||||||
will be changed into Private Assemblies. This means
|
|
||||||
the exact versions of these assemblies will always be
|
|
||||||
used, and any newer versions installed on user
|
|
||||||
machines at the system level will be ignored.
|
|
||||||
--win-no-prefer-redirects
|
|
||||||
While searching for Shared or Private Assemblies to
|
|
||||||
bundle into the application, PyInstaller will prefer
|
|
||||||
not to follow policies that redirect to newer
|
|
||||||
versions, and will try to bundle the exact versions of
|
|
||||||
the assembly.
|
|
||||||
|
|
||||||
Mac OS X specific options:
|
|
||||||
--osx-bundle-identifier BUNDLE_IDENTIFIER
|
|
||||||
Mac OS X .app bundle identifier is used as the default
|
|
||||||
unique program name for code signing purposes. The
|
|
||||||
usual form is a hierarchical name in reverse DNS
|
|
||||||
notation. For example:
|
|
||||||
com.mycompany.department.appname (default: first
|
|
||||||
script's basename)
|
|
||||||
--target-architecture ARCH, --target-arch ARCH
|
|
||||||
Target architecture (macOS only; valid values: x86_64,
|
|
||||||
arm64, universal2). Enables switching between
|
|
||||||
universal2 and single-arch version of frozen
|
|
||||||
application (provided python installation supports the
|
|
||||||
target architecture). If not target architecture is
|
|
||||||
not specified, the current running architecture is
|
|
||||||
targeted.
|
|
||||||
--codesign-identity IDENTITY
|
|
||||||
Code signing identity (macOS only). Use the provided
|
|
||||||
identity to sign collected binaries and generated
|
|
||||||
executable. If signing identity is not provided, ad-
|
|
||||||
hoc signing is performed instead.
|
|
||||||
--osx-entitlements-file FILENAME
|
|
||||||
Entitlements file to use when code-signing the
|
|
||||||
collected binaries (macOS only).
|
|
||||||
|
|
||||||
Rarely used special options:
|
|
||||||
--runtime-tmpdir PATH
|
|
||||||
Where to extract libraries and support files in
|
|
||||||
`onefile`-mode. If this option is given, the
|
|
||||||
bootloader will ignore any temp-folder location
|
|
||||||
defined by the run-time OS. The ``_MEIxxxxxx``-folder
|
|
||||||
will be created here. Please use this option only if
|
|
||||||
you know what you are doing.
|
|
||||||
--bootloader-ignore-signals
|
|
||||||
Tell the bootloader to ignore signals rather than
|
|
||||||
forwarding them to the child process. Useful in
|
|
||||||
situations where e.g. a supervisor process signals
|
|
||||||
both the bootloader and child (e.g. via a process
|
|
||||||
group) to avoid signalling the child twice.
|
|
0
rscder/gui/__init__.py
Normal file
0
rscder/gui/__init__.py
Normal file
22
rscder/gui/layercombox.py
Normal file
22
rscder/gui/layercombox.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from PyQt5.QtWidgets import QComboBox
|
||||||
|
from rscder.utils.project import Project
|
||||||
|
class LayerCombox(QComboBox):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.addItem('---', None)
|
||||||
|
|
||||||
|
for layer in Project().layers.values():
|
||||||
|
self.addItem(layer.name, layer.id)
|
||||||
|
|
||||||
|
self.currentIndexChanged.connect(self.on_changed)
|
||||||
|
|
||||||
|
self.current_layer = None
|
||||||
|
|
||||||
|
def on_changed(self, index):
|
||||||
|
if index == 0:
|
||||||
|
self.current_layer = None
|
||||||
|
else:
|
||||||
|
self.current_layer = Project().layers[self.itemData(index)]
|
||||||
|
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
|
import pdb
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
from PyQt5.QtCore import Qt,QModelIndex
|
from PyQt5.QtCore import Qt,QModelIndex
|
||||||
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QCursor
|
||||||
from PyQt5.QtWidgets import (QTreeView, QTreeWidgetItem, QAbstractItemView, QHeaderView, QStyleFactory)
|
from PyQt5.QtWidgets import (QTreeView, QTreeWidgetItem, QAbstractItemView, QHeaderView, QStyleFactory)
|
||||||
from rscder.gui.actions import get_action_manager
|
from rscder.gui.actions import get_action_manager
|
||||||
|
|
||||||
@ -9,6 +10,15 @@ from rscder.utils.project import PairLayer, Project
|
|||||||
|
|
||||||
class LayerTree(QtWidgets.QWidget):
|
class LayerTree(QtWidgets.QWidget):
|
||||||
|
|
||||||
|
LAYER_TOOT = 0
|
||||||
|
SUB_RASTER = 1
|
||||||
|
RESULT = 2
|
||||||
|
LEFT_RASTER = 0
|
||||||
|
RIGHT_RASTER = 1
|
||||||
|
GRID = 3
|
||||||
|
|
||||||
|
tree_changed = QtCore.pyqtSignal(str)
|
||||||
|
result_clicked = QtCore.pyqtSignal(str, int)
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
# self.tree_view = QTreeView(self)
|
# self.tree_view = QTreeView(self)
|
||||||
@ -16,7 +26,8 @@ class LayerTree(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.tree.setColumnCount(1)
|
self.tree.setColumnCount(1)
|
||||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
self.customContextMenuRequested.connect(self.right_menu_show)
|
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
self.tree.customContextMenuRequested.connect(self.right_menu_show)
|
||||||
self.root=QTreeWidgetItem(self.tree)
|
self.root=QTreeWidgetItem(self.tree)
|
||||||
self.tree.setHeaderHidden(True)
|
self.tree.setHeaderHidden(True)
|
||||||
# self.tree.setHeaderLabels(['图层'])
|
# self.tree.setHeaderLabels(['图层'])
|
||||||
@ -31,36 +42,123 @@ class LayerTree(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.tree.addTopLevelItem(self.root)
|
self.tree.addTopLevelItem(self.root)
|
||||||
|
|
||||||
self.tree.clicked.connect(self.onClicked)
|
# self.tree.clicked.connect(self.onClicked)
|
||||||
|
self.tree.itemClicked.connect(self.onItemClicked)
|
||||||
|
self.tree.itemChanged.connect(self.onItemChanged)
|
||||||
|
|
||||||
layout = QtWidgets.QGridLayout()
|
layout = QtWidgets.QGridLayout()
|
||||||
layout.addWidget(self.tree)
|
layout.addWidget(self.tree)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
self.setLayoutDirection(Qt.LeftToRight)
|
self.setLayoutDirection(Qt.LeftToRight)
|
||||||
|
self.is_in_add_layer = False
|
||||||
|
|
||||||
def onClicked(self,index):
|
def onItemClicked(self, item:QtWidgets.QTreeWidgetItem, column):
|
||||||
print(index.row())
|
|
||||||
item = self.tree.currentItem()
|
|
||||||
if item == self.root:
|
if item == self.root:
|
||||||
return
|
return
|
||||||
layer_id = str(item.data(0, Qt.UserRole))
|
root = item
|
||||||
layer = Project().layers[layer_id]
|
if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT:
|
||||||
print(layer.l1_name)
|
root = item.parent()
|
||||||
print(layer.l2_name)
|
if item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT:
|
||||||
|
return
|
||||||
|
layer = Project().layers[root.data(0, Qt.UserRole + 1)]
|
||||||
|
Project().current_layer = layer
|
||||||
|
if item.data(0, Qt.UserRole) == LayerTree.RESULT:
|
||||||
|
# result = layer.results[item.data(0, Qt.UserRole + 1)]
|
||||||
|
self.result_clicked.emit(layer.id, item.data(0, Qt.UserRole + 1))
|
||||||
|
|
||||||
|
|
||||||
|
def onItemChanged(self, item:QtWidgets.QTreeWidgetItem, column):
|
||||||
|
if self.is_in_add_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(layer.id)
|
||||||
|
|
||||||
def add_layer(self, layer:str):
|
def add_layer(self, layer:str):
|
||||||
|
# self.tree.it
|
||||||
|
self.is_in_add_layer = True
|
||||||
layer:PairLayer = Project().layers[layer]
|
layer:PairLayer = Project().layers[layer]
|
||||||
item1 = QtWidgets.QTreeWidgetItem(self.root)
|
item_root = QtWidgets.QTreeWidgetItem(self.root)
|
||||||
item1.setText(0, layer.l1_name)
|
item_root.setText(0,layer.name)
|
||||||
item1.setCheckState(0, Qt.Checked)
|
item_root.setData(0, Qt.UserRole, LayerTree.LAYER_TOOT)
|
||||||
item2 = QtWidgets.QTreeWidgetItem(self.root)
|
item_root.setData(0, Qt.UserRole + 1, layer.id)
|
||||||
item2.setText(0, layer.l2_name)
|
item_root.setCheckState(0, Qt.Checked if layer.enable else Qt.Unchecked)
|
||||||
item2.setCheckState(0, Qt.Checked)
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
item2 = QtWidgets.QTreeWidgetItem(item_root)
|
||||||
|
item2.setText(0, layer.l2_name)
|
||||||
|
item2.setCheckState(0, Qt.Checked if layer.l2_enable else Qt.Unchecked)
|
||||||
|
item1.setData(0, Qt.UserRole, LayerTree.SUB_RASTER)
|
||||||
|
item1.setData(0, Qt.UserRole + 1, LayerTree.RIGHT_RASTER)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
item1.setData(0, Qt.UserRole, layer.id)
|
|
||||||
item2.setData(0, Qt.UserRole, layer.id)
|
|
||||||
self.tree.expandAll()
|
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
|
||||||
|
print(layer_root.text(0))
|
||||||
|
if layer_root is None:
|
||||||
|
self.add_layer(layer.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
layer_root.setText(0,layer.name)
|
||||||
|
|
||||||
|
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 clear(self):
|
def clear(self):
|
||||||
self.tree.clear()
|
self.tree.clear()
|
||||||
self.root = QTreeWidgetItem(self.tree)
|
self.root = QTreeWidgetItem(self.tree)
|
||||||
@ -70,17 +168,37 @@ class LayerTree(QtWidgets.QWidget):
|
|||||||
def right_menu_show(self, position):
|
def right_menu_show(self, position):
|
||||||
rightMenu = QtWidgets.QMenu(self)
|
rightMenu = QtWidgets.QMenu(self)
|
||||||
# QAction = QtWidgets.QAction(self.menuBar1)
|
# QAction = QtWidgets.QAction(self.menuBar1)
|
||||||
item = self.tree.currentItem()
|
item = self.tree.itemAt(position)
|
||||||
|
|
||||||
action_manager = get_action_manager()
|
action_manager = get_action_manager()
|
||||||
actions = []
|
actions = []
|
||||||
if item == self.root:
|
data_load_action = action_manager.get_action('&数据加载', 'File')
|
||||||
data_load_action = action_manager.get_action('&数据加载', 'File')
|
actions.append(data_load_action)
|
||||||
actions.append(data_load_action)
|
if item is None:
|
||||||
|
print('nothing')
|
||||||
else:
|
else:
|
||||||
pass
|
if item == self.root:
|
||||||
|
pass
|
||||||
|
elif item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT:
|
||||||
|
actions.append(QtWidgets.QAction('&缩放至该图层', self))
|
||||||
|
|
||||||
|
actions.append(QtWidgets.QAction('&重命名', self))
|
||||||
|
actions.append(QtWidgets.QAction('&删除', self))
|
||||||
|
elif item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER:
|
||||||
|
actions.append(QtWidgets.QAction('&缩放至该图层', self))
|
||||||
|
|
||||||
|
actions.append(QtWidgets.QAction('&重命名', self))
|
||||||
|
actions.append(QtWidgets.QAction('&删除', self))
|
||||||
|
elif item.data(0, Qt.UserRole) == LayerTree.RESULT:
|
||||||
|
actions.append(QtWidgets.QAction('&缩放至该图层', self))
|
||||||
|
|
||||||
|
actions.append(QtWidgets.QAction('&重命名', self))
|
||||||
|
actions.append(QtWidgets.QAction('&导出', self))
|
||||||
|
actions.append(QtWidgets.QAction('&删除', self))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for action in actions:
|
for action in actions:
|
||||||
rightMenu.addAction(action)
|
rightMenu.addAction(action)
|
||||||
|
|
||||||
rightMenu.exec_(self.mapToGlobal(position))
|
rightMenu.exec_(QCursor.pos())
|
||||||
|
|
@ -33,6 +33,11 @@ class MainWindow(QMainWindow):
|
|||||||
self.layer_tree,
|
self.layer_tree,
|
||||||
self.message_box,
|
self.message_box,
|
||||||
self.result_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.action_manager = ActionManager(
|
self.action_manager = ActionManager(
|
||||||
self.double_map,
|
self.double_map,
|
||||||
self.layer_tree,
|
self.layer_tree,
|
||||||
|
@ -66,8 +66,9 @@ class DoubleCanvas(QWidget):
|
|||||||
action.setChecked(self.grid_show)
|
action.setChecked(self.grid_show)
|
||||||
if self.grid_show:
|
if self.grid_show:
|
||||||
for layer in Project().layers.values():
|
for layer in Project().layers.values():
|
||||||
self.mapcanva1.add_grid_layer(layer.grid_layer.grid_layer)
|
if layer.grid_enable:
|
||||||
self.mapcanva2.add_grid_layer(layer.grid_layer.grid_layer)
|
self.mapcanva1.add_grid_layer(layer.grid_layer.grid_layer)
|
||||||
|
self.mapcanva2.add_grid_layer(layer.grid_layer.grid_layer)
|
||||||
else:
|
else:
|
||||||
self.mapcanva1.remove_grid_layer()
|
self.mapcanva1.remove_grid_layer()
|
||||||
self.mapcanva2.remove_grid_layer()
|
self.mapcanva2.remove_grid_layer()
|
||||||
@ -99,15 +100,47 @@ class DoubleCanvas(QWidget):
|
|||||||
self.mapcanva2.setMapTool(QgsMapToolZoom(self.mapcanva2, True))
|
self.mapcanva2.setMapTool(QgsMapToolZoom(self.mapcanva2, True))
|
||||||
|
|
||||||
def add_layer(self, layer:str):
|
def add_layer(self, layer:str):
|
||||||
layer = Project().layers[layer]
|
layer:PairLayer = Project().layers[layer]
|
||||||
if not self.mapcanva1.is_main and not self.mapcanva2.is_main:
|
if not layer.enable:
|
||||||
|
return
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not self.mapcanva1.is_main and not self.mapcanva2.is_main:
|
||||||
self.mapcanva1.is_main = True
|
self.mapcanva1.is_main = True
|
||||||
self.mapcanva1.add_layer(layer.l1)
|
|
||||||
self.mapcanva2.add_layer(layer.l2)
|
if layer.l1_enable:
|
||||||
if self.grid_show:
|
self.mapcanva1.add_layer(layer.l1)
|
||||||
|
if layer.l2_enable:
|
||||||
|
self.mapcanva2.add_layer(layer.l2)
|
||||||
|
if layer.grid_enable and self.grid_show:
|
||||||
self.mapcanva1.add_grid_layer(layer.grid_layer.grid_layer)
|
self.mapcanva1.add_grid_layer(layer.grid_layer.grid_layer)
|
||||||
self.mapcanva2.add_grid_layer(layer.grid_layer.grid_layer)
|
self.mapcanva2.add_grid_layer(layer.grid_layer.grid_layer)
|
||||||
|
for r in layer.results:
|
||||||
|
if r.enable:
|
||||||
|
self.mapcanva1.add_layer(r.layer)
|
||||||
|
self.mapcanva2.add_layer(r.layer)
|
||||||
|
# self.mapcanva1.set_extent(layer.l1.extent())
|
||||||
|
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)
|
||||||
|
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 layer_changed(self, layer:str):
|
||||||
|
self.add_layer(layer)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.mapcanva1.clear()
|
self.mapcanva1.clear()
|
||||||
|
@ -4,7 +4,7 @@ from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal
|
|||||||
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
||||||
from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QAbstractItemView, QHeaderView, QStyleFactory)
|
from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QAbstractItemView, QHeaderView, QStyleFactory)
|
||||||
|
|
||||||
from rscder.utils.project import PairLayer, ResultLayer
|
from rscder.utils.project import PairLayer, Project, ResultLayer
|
||||||
|
|
||||||
class ResultTable(QtWidgets.QWidget):
|
class ResultTable(QtWidgets.QWidget):
|
||||||
|
|
||||||
@ -29,11 +29,15 @@ class ResultTable(QtWidgets.QWidget):
|
|||||||
layout = QtWidgets.QVBoxLayout(self)
|
layout = QtWidgets.QVBoxLayout(self)
|
||||||
layout.addWidget(self.tablewidget)
|
layout.addWidget(self.tablewidget)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
self.result = None
|
||||||
|
self.is_in_set_data = False
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
pass
|
self.tablewidget.clear()
|
||||||
|
|
||||||
def onChanged(self, row, col):
|
def onChanged(self, row, col):
|
||||||
|
if self.is_in_set_data:
|
||||||
|
return
|
||||||
if col == 3:
|
if col == 3:
|
||||||
item_idx = row
|
item_idx = row
|
||||||
item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked
|
item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked
|
||||||
@ -41,7 +45,8 @@ class ResultTable(QtWidgets.QWidget):
|
|||||||
self.tablewidget.item(row, col).setBackground(Qt.yellow)
|
self.tablewidget.item(row, col).setBackground(Qt.yellow)
|
||||||
else:
|
else:
|
||||||
self.tablewidget.item(row, col).setBackground(Qt.green)
|
self.tablewidget.item(row, col).setBackground(Qt.green)
|
||||||
self.on_item_changed.emit({'idx':item_idx, 'status':item_status})
|
# print(item_idx, item_status)
|
||||||
|
self.result.update({'row':item_idx, 'value':item_status})
|
||||||
|
|
||||||
def onClicked(self, row, col):
|
def onClicked(self, row, col):
|
||||||
if col == 3:
|
if col == 3:
|
||||||
@ -50,9 +55,18 @@ class ResultTable(QtWidgets.QWidget):
|
|||||||
def onDoubleClicked(self, row, col):
|
def onDoubleClicked(self, row, col):
|
||||||
x = self.tablewidget.item(row, 0).text()
|
x = self.tablewidget.item(row, 0).text()
|
||||||
y = self.tablewidget.item(row, 1).text()
|
y = self.tablewidget.item(row, 1).text()
|
||||||
self.on_item_click.emit({'x':x, 'y':y})
|
self.on_item_click.emit({'x':float(x), 'y':float(y)})
|
||||||
|
|
||||||
|
def on_result(self, layer_id, result_id):
|
||||||
|
self.is_in_set_data = True
|
||||||
|
result = Project().layers[layer_id].results[result_id]
|
||||||
|
self.result = result
|
||||||
|
self.clear()
|
||||||
|
self.set_data(result)
|
||||||
def set_data(self, data:ResultLayer):
|
def set_data(self, data:ResultLayer):
|
||||||
|
self.is_in_set_data = True
|
||||||
|
if data.layer_type != ResultLayer.POINT:
|
||||||
|
return
|
||||||
self.tablewidget.setRowCount(len(data.data))
|
self.tablewidget.setRowCount(len(data.data))
|
||||||
# print(len(data.data))
|
# print(len(data.data))
|
||||||
self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))])
|
self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))])
|
||||||
@ -73,5 +87,4 @@ class ResultTable(QtWidgets.QWidget):
|
|||||||
self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
self.tablewidget.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
self.tablewidget.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
|
||||||
|
self.is_in_set_data = False
|
||||||
|
|
@ -2,9 +2,10 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
import uuid
|
import uuid
|
||||||
|
import numpy as np
|
||||||
from osgeo import gdal, gdal_array
|
from osgeo import gdal, gdal_array
|
||||||
from rscder.utils.setting import Settings
|
from rscder.utils.setting import Settings
|
||||||
from qgis.core import QgsRasterLayer, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY
|
from qgis.core import QgsRasterLayer, QgsMarkerSymbol, QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal
|
from PyQt5.QtCore import QObject, pyqtSignal
|
||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
import yaml
|
import yaml
|
||||||
@ -32,6 +33,7 @@ class Project(QObject):
|
|||||||
self.root = str(Path(Settings.General().root)/'default')
|
self.root = str(Path(Settings.General().root)/'default')
|
||||||
self.file_mode = Project.ABSOLUTE_MODE
|
self.file_mode = Project.ABSOLUTE_MODE
|
||||||
self.layers:Dict[str, PairLayer] = dict()
|
self.layers:Dict[str, PairLayer] = dict()
|
||||||
|
self.current_layer = None
|
||||||
|
|
||||||
def connect(self, pair_canvas,
|
def connect(self, pair_canvas,
|
||||||
layer_tree,
|
layer_tree,
|
||||||
@ -45,6 +47,15 @@ class Project(QObject):
|
|||||||
self.layer_load.connect(layer_tree.add_layer)
|
self.layer_load.connect(layer_tree.add_layer)
|
||||||
self.layer_load.connect(pair_canvas.add_layer)
|
self.layer_load.connect(pair_canvas.add_layer)
|
||||||
# self.layer_load.connect(message_box.add_layer)
|
# self.layer_load.connect(message_box.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
|
||||||
|
|
||||||
def setup(self, path = None, name = None):
|
def setup(self, path = None, name = None):
|
||||||
self.is_init = True
|
self.is_init = True
|
||||||
@ -71,7 +82,6 @@ class Project(QObject):
|
|||||||
'max_threads': self.max_threads,
|
'max_threads': self.max_threads,
|
||||||
'root': self.root,
|
'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(None if self.file_mode == Project.ABSOLUTE_MODE else self.root) for layer in self.layers.values() ],
|
||||||
'results': []
|
|
||||||
}
|
}
|
||||||
with open(self.file, 'w') as f:
|
with open(self.file, 'w') as f:
|
||||||
yaml.safe_dump(data_dict, f)
|
yaml.safe_dump(data_dict, f)
|
||||||
@ -89,22 +99,25 @@ class Project(QObject):
|
|||||||
self.result_table.clear()
|
self.result_table.clear()
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
with open(self.file, 'r') as f:
|
try:
|
||||||
data = yaml.safe_load(f)
|
with open(self.file, 'r') as f:
|
||||||
if data is None:
|
data = yaml.safe_load(f)
|
||||||
return
|
if data is None:
|
||||||
# data = yaml.safe_load(open(self.file, 'r'))
|
return
|
||||||
self.cell_size = data['cell_size']
|
# data = yaml.safe_load(open(self.file, 'r'))
|
||||||
self.max_memory = data['max_memory']
|
self.cell_size = data['cell_size']
|
||||||
self.max_threads = data['max_threads']
|
self.max_memory = data['max_memory']
|
||||||
self.root = data['root']
|
self.max_threads = data['max_threads']
|
||||||
self.layers = dict()
|
self.root = data['root']
|
||||||
for layer in data['layers']:
|
self.layers = dict()
|
||||||
player = PairLayer.from_dict(layer, None if self.file_mode == Project.ABSOLUTE_MODE else self.root)
|
for layer in data['layers']:
|
||||||
if player.check():
|
player = PairLayer.from_dict(layer, None if self.file_mode == Project.ABSOLUTE_MODE else self.root)
|
||||||
self.layers[player.id] = player
|
if player.check():
|
||||||
self.layer_load.emit(player.id)
|
self.layers[player.id] = player
|
||||||
|
self.layer_load.emit(player.id)
|
||||||
|
except Exception as e:
|
||||||
|
self.message_box.error(str(e))
|
||||||
|
self.clear()
|
||||||
def add_layer(self, pth1, pth2):
|
def add_layer(self, pth1, pth2):
|
||||||
# self.root = str(Path(pth1).parent)
|
# self.root = str(Path(pth1).parent)
|
||||||
|
|
||||||
@ -205,9 +218,142 @@ class ResultLayer:
|
|||||||
POINT = 0
|
POINT = 0
|
||||||
RASTER = 1
|
RASTER = 1
|
||||||
|
|
||||||
def __init__(self, layer_type):
|
def __init__(self, name, layer_type = POINT):
|
||||||
self.layer_type = layer_type
|
self.layer_type = layer_type
|
||||||
self.data = []
|
self.data = None
|
||||||
|
self.layer = None
|
||||||
|
self.name = name
|
||||||
|
self.path = None
|
||||||
|
self.wkt = None
|
||||||
|
self.enable = False
|
||||||
|
|
||||||
|
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()
|
||||||
|
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')
|
||||||
|
|
||||||
|
def format_point_layer(self, layer):
|
||||||
|
layer.setLabelsEnabled(True)
|
||||||
|
lyr = QgsPalLayerSettings()
|
||||||
|
lyr.enabled = True
|
||||||
|
lyr.fieldName = 'id'
|
||||||
|
lyr.placement = QgsPalLayerSettings.OverPoint
|
||||||
|
lyr.textNamedStyle = 'Medium'
|
||||||
|
text_format = QgsTextFormat()
|
||||||
|
text_format.color = QColor('#ffffff')
|
||||||
|
text_format.background().color = QColor('#000000')
|
||||||
|
text_format.buffer().setEnabled(True)
|
||||||
|
text_format.buffer().setSize(1)
|
||||||
|
text_format.buffer().setOpacity(0.5)
|
||||||
|
lyr.setFormat(text_format)
|
||||||
|
root = QgsRuleBasedLabeling.Rule(QgsPalLayerSettings())
|
||||||
|
rule = QgsRuleBasedLabeling.Rule(lyr)
|
||||||
|
rule.setDescription('label')
|
||||||
|
root.appendChild(rule)
|
||||||
|
#Apply label configuration
|
||||||
|
rules = QgsRuleBasedLabeling(root)
|
||||||
|
layer.setLabeling(rules)
|
||||||
|
|
||||||
|
def set_render(self, layer):
|
||||||
|
symbol = QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '5'})
|
||||||
|
render = QgsSingleSymbolRenderer(symbol)
|
||||||
|
layer.setRenderer(render)
|
||||||
|
|
||||||
|
def load_point_file(self):
|
||||||
|
data = np.loadtxt(self.path, delimiter=',', skiprows=1)
|
||||||
|
if data is None:
|
||||||
|
return
|
||||||
|
self.data = data
|
||||||
|
self.make_point_layer()
|
||||||
|
|
||||||
|
def make_point_layer(self):
|
||||||
|
if self.wkt is not None:
|
||||||
|
crs = QgsCoordinateReferenceSystem()
|
||||||
|
crs.createFromString('WKT:{}'.format(self.wkt))
|
||||||
|
else:
|
||||||
|
crs = QgsCoordinateReferenceSystem()
|
||||||
|
|
||||||
|
uri = 'Point?crs={}'.format(crs.toProj())
|
||||||
|
layer = QgsVectorLayer(uri, self.name, "memory")
|
||||||
|
if not layer.isValid():
|
||||||
|
Project().message_box.error('Failed to create layer')
|
||||||
|
return
|
||||||
|
self.format_point_layer(layer)
|
||||||
|
layer.startEditing()
|
||||||
|
features = []
|
||||||
|
for i, d in enumerate(self.data):
|
||||||
|
point = QgsFeature(i)
|
||||||
|
point.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1])))
|
||||||
|
# point.setAttribute('id', i)
|
||||||
|
features.append(point)
|
||||||
|
layer.addFeatures(features)
|
||||||
|
layer.commitChanges()
|
||||||
|
self.set_render(layer)
|
||||||
|
self.layer = layer
|
||||||
|
|
||||||
|
def update_point_layer(self):
|
||||||
|
if self.layer is None:
|
||||||
|
return
|
||||||
|
self.layer.startEditing()
|
||||||
|
add_features = []
|
||||||
|
delete_features = []
|
||||||
|
for i, d in enumerate(self.data):
|
||||||
|
feature = self.layer.getFeature(i+1)
|
||||||
|
if d[-1]:
|
||||||
|
if feature is None:
|
||||||
|
feature = QgsFeature(i)
|
||||||
|
feature.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1])))
|
||||||
|
# feature.setAttribute('id', i)
|
||||||
|
add_features.append(feature)
|
||||||
|
else:
|
||||||
|
if feature is not None:
|
||||||
|
delete_features.append(feature.id())
|
||||||
|
if len(add_features) > 0:
|
||||||
|
self.layer.addFeatures(add_features)
|
||||||
|
if len(delete_features) > 0:
|
||||||
|
self.layer.deleteFeatures(delete_features)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(data, root = None):
|
||||||
|
result = ResultLayer(data['name'], 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 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))
|
||||||
|
}
|
||||||
|
# def load_file(self, path):
|
||||||
|
|
||||||
|
|
||||||
class PairLayer:
|
class PairLayer:
|
||||||
|
|
||||||
@ -219,14 +365,19 @@ class PairLayer:
|
|||||||
'l1_name': self.l1_name,
|
'l1_name': self.l1_name,
|
||||||
'l2_name': self.l2_name,
|
'l2_name': self.l2_name,
|
||||||
'cell_size': self.cell_size,
|
'cell_size': self.cell_size,
|
||||||
|
'results': [r.to_dict(root) for r in self.results],
|
||||||
|
'name': self.name
|
||||||
|
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
'pth1': relative_path(self.pth1, root),
|
'pth1': relative_path(self.pth1, root),
|
||||||
'pth2': relative_path(self.pth2, root),
|
'pth2': relative_path(self.pth2, root),
|
||||||
|
'name': self.name,
|
||||||
'l1_name': self.l1_name,
|
'l1_name': self.l1_name,
|
||||||
'l2_name': self.l2_name,
|
'l2_name': self.l2_name,
|
||||||
'cell_size': self.cell_size,
|
'cell_size': self.cell_size,
|
||||||
|
'results': [r.to_dict(root) for r in self.results]
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -237,23 +388,37 @@ class PairLayer:
|
|||||||
layer = PairLayer(os.path.join(root, data['pth1']), os.path.join(root, data['pth2']), data['cell_size'])
|
layer = PairLayer(os.path.join(root, data['pth1']), os.path.join(root, data['pth2']), data['cell_size'])
|
||||||
layer.l1_name = data['l1_name']
|
layer.l1_name = data['l1_name']
|
||||||
layer.l2_name = data['l2_name']
|
layer.l2_name = data['l2_name']
|
||||||
|
layer.name = data['name']
|
||||||
|
|
||||||
|
for r in data['results']:
|
||||||
|
layer.results.append(ResultLayer.from_dict(r, root))
|
||||||
# layer.grid_layer = GridLayer.from_dict(data['grid_layer'])
|
# layer.grid_layer = GridLayer.from_dict(data['grid_layer'])
|
||||||
return layer
|
return layer
|
||||||
def __init__(self, pth1, pth2, cell_size) -> None:
|
def __init__(self, pth1, pth2, cell_size) -> None:
|
||||||
self.pth1 = pth1
|
self.pth1 = pth1
|
||||||
self.pth2 = pth2
|
self.pth2 = pth2
|
||||||
|
self.enable = True
|
||||||
|
self.l1_enable = True
|
||||||
|
self.l2_enable = True
|
||||||
|
self.grid_enable = True
|
||||||
self.id = str(uuid.uuid1())
|
self.id = str(uuid.uuid1())
|
||||||
|
self.name = '{}-{}'.format(os.path.basename(pth1), os.path.basename(pth2))
|
||||||
self.l1_name = os.path.basename(pth1)
|
self.l1_name = os.path.basename(pth1)
|
||||||
self.l2_name = os.path.basename(pth2)
|
self.l2_name = os.path.basename(pth2)
|
||||||
|
|
||||||
self.cell_size = cell_size
|
self.cell_size = cell_size
|
||||||
|
|
||||||
# self.grid_layer = GridLayer(cell_size)
|
|
||||||
|
|
||||||
self.msg = ''
|
self.msg = ''
|
||||||
self.checked = False
|
self.checked = False
|
||||||
|
|
||||||
|
self.xsize = 0
|
||||||
|
self.ysize = 0
|
||||||
|
self.xres = 0
|
||||||
|
self.yres = 0
|
||||||
|
|
||||||
|
self.wkt = None
|
||||||
|
|
||||||
|
self.results:List[ResultLayer] = []
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
if self.checked:
|
if self.checked:
|
||||||
return self.checked
|
return self.checked
|
||||||
@ -274,6 +439,14 @@ class PairLayer:
|
|||||||
self.msg = '图层尺寸不一致'
|
self.msg = '图层尺寸不一致'
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
self.xsize = ds1.RasterXSize
|
||||||
|
self.ysize = ds1.RasterYSize
|
||||||
|
|
||||||
|
self.xres = ds1.GetGeoTransform()[1]
|
||||||
|
self.yres = ds1.GetGeoTransform()[5]
|
||||||
|
|
||||||
|
self.wkt = ds1.GetProjection()
|
||||||
|
|
||||||
self.grid_layer = GridLayer(self.cell_size, ds1)
|
self.grid_layer = GridLayer(self.cell_size, ds1)
|
||||||
|
|
||||||
del ds1
|
del ds1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user