BERATools 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- beratools/__init__.py +9 -0
- beratools/core/__init__.py +0 -0
- beratools/core/algo_centerline.py +351 -0
- beratools/core/constants.py +86 -0
- beratools/core/dijkstra_algorithm.py +460 -0
- beratools/core/logger.py +85 -0
- beratools/core/tool_base.py +133 -0
- beratools/gui/__init__.py +15 -0
- beratools/gui/batch_processing_dlg.py +463 -0
- beratools/gui/beratools.json +2300 -0
- beratools/gui/bt_data.py +487 -0
- beratools/gui/bt_gui_main.py +691 -0
- beratools/gui/cli.py +18 -0
- beratools/gui/gui.json +8 -0
- beratools/gui/img/BERALogo.png +0 -0
- beratools/gui/img/closed.gif +0 -0
- beratools/gui/img/closed.png +0 -0
- beratools/gui/img/open.gif +0 -0
- beratools/gui/img/open.png +0 -0
- beratools/gui/img/tool.gif +0 -0
- beratools/gui/img/tool.png +0 -0
- beratools/gui/map_window.py +146 -0
- beratools/gui/tool_widgets.py +493 -0
- beratools/gui_tk/ASCII Banners.txt +248 -0
- beratools/gui_tk/__init__.py +20 -0
- beratools/gui_tk/beratools_main.py +515 -0
- beratools/gui_tk/bt_widgets.py +442 -0
- beratools/gui_tk/cli.py +18 -0
- beratools/gui_tk/gui.json +8 -0
- beratools/gui_tk/img/BERALogo.png +0 -0
- beratools/gui_tk/img/closed.gif +0 -0
- beratools/gui_tk/img/closed.png +0 -0
- beratools/gui_tk/img/open.gif +0 -0
- beratools/gui_tk/img/open.png +0 -0
- beratools/gui_tk/img/tool.gif +0 -0
- beratools/gui_tk/img/tool.png +0 -0
- beratools/gui_tk/main.py +14 -0
- beratools/gui_tk/map_window.py +144 -0
- beratools/gui_tk/runner.py +1481 -0
- beratools/gui_tk/tooltip.py +55 -0
- beratools/third_party/pyqtlet2/__init__.py +9 -0
- beratools/third_party/pyqtlet2/leaflet/__init__.py +26 -0
- beratools/third_party/pyqtlet2/leaflet/control/__init__.py +6 -0
- beratools/third_party/pyqtlet2/leaflet/control/control.py +59 -0
- beratools/third_party/pyqtlet2/leaflet/control/draw.py +52 -0
- beratools/third_party/pyqtlet2/leaflet/control/layers.py +20 -0
- beratools/third_party/pyqtlet2/leaflet/core/Parser.py +24 -0
- beratools/third_party/pyqtlet2/leaflet/core/__init__.py +2 -0
- beratools/third_party/pyqtlet2/leaflet/core/evented.py +180 -0
- beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +5 -0
- beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +34 -0
- beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +1 -0
- beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +30 -0
- beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +18 -0
- beratools/third_party/pyqtlet2/leaflet/layer/layer.py +105 -0
- beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +45 -0
- beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +1 -0
- beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +91 -0
- beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +2 -0
- beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +4 -0
- beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +16 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +5 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +15 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +18 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +5 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +14 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +18 -0
- beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +14 -0
- beratools/third_party/pyqtlet2/leaflet/map/__init__.py +1 -0
- beratools/third_party/pyqtlet2/leaflet/map/map.py +220 -0
- beratools/third_party/pyqtlet2/mapwidget.py +45 -0
- beratools/third_party/pyqtlet2/web/custom.js +43 -0
- beratools/third_party/pyqtlet2/web/map.html +23 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +656 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +6 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +14 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +4 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +22 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +43 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +20 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +156 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +10 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +10 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +22 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +57 -0
- beratools/tools/Beratools_r_script.r +1120 -0
- beratools/tools/Ht_metrics.py +116 -0
- beratools/tools/__init__.py +7 -0
- beratools/tools/batch_processing.py +132 -0
- beratools/tools/canopy_threshold_relative.py +670 -0
- beratools/tools/canopycostraster.py +222 -0
- beratools/tools/centerline.py +176 -0
- beratools/tools/common.py +885 -0
- beratools/tools/fl_regen_csf.py +428 -0
- beratools/tools/forest_line_attributes.py +408 -0
- beratools/tools/forest_line_ecosite.py +216 -0
- beratools/tools/lapis_all.py +103 -0
- beratools/tools/least_cost_path_from_chm.py +152 -0
- beratools/tools/line_footprint_absolute.py +363 -0
- beratools/tools/line_footprint_fixed.py +282 -0
- beratools/tools/line_footprint_functions.py +720 -0
- beratools/tools/line_footprint_relative.py +64 -0
- beratools/tools/ln_relative_metrics.py +615 -0
- beratools/tools/r_cal_lpi_elai.r +25 -0
- beratools/tools/r_generate_pd_focalraster.r +101 -0
- beratools/tools/r_interface.py +80 -0
- beratools/tools/r_point_density.r +9 -0
- beratools/tools/rpy_chm2trees.py +86 -0
- beratools/tools/rpy_dsm_chm_by.py +81 -0
- beratools/tools/rpy_dtm_by.py +63 -0
- beratools/tools/rpy_find_cellsize.py +43 -0
- beratools/tools/rpy_gnd_csf.py +74 -0
- beratools/tools/rpy_hummock_hollow.py +85 -0
- beratools/tools/rpy_hummock_hollow_raster.py +71 -0
- beratools/tools/rpy_las_info.py +51 -0
- beratools/tools/rpy_laz2las.py +40 -0
- beratools/tools/rpy_lpi_elai_lascat.py +466 -0
- beratools/tools/rpy_normalized_lidar_by.py +56 -0
- beratools/tools/rpy_percent_above_dbh.py +80 -0
- beratools/tools/rpy_points2trees.py +88 -0
- beratools/tools/rpy_vegcoverage.py +94 -0
- beratools/tools/tiler.py +206 -0
- beratools/tools/tool_template.py +54 -0
- beratools/tools/vertex_optimization.py +620 -0
- beratools/tools/zonal_threshold.py +144 -0
- beratools-0.2.0.dist-info/METADATA +63 -0
- beratools-0.2.0.dist-info/RECORD +142 -0
- beratools-0.2.0.dist-info/WHEEL +4 -0
- beratools-0.2.0.dist-info/entry_points.txt +2 -0
- beratools-0.2.0.dist-info/licenses/LICENSE +22 -0
beratools/gui/gui.json
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
os.environ['QT_API'] = 'pyqt5'
|
|
5
|
+
from qtpy.QtWidgets import (QApplication, QVBoxLayout, QHBoxLayout, QWidget, QTreeWidget, QTreeWidgetItem,
|
|
6
|
+
QPushButton, QGroupBox, QDialog, QDialogButtonBox)
|
|
7
|
+
from qtpy.QtCore import (Qt, Signal)
|
|
8
|
+
from beratools.third_party.pyqtlet2 import L, MapWidget
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MapWindow(QDialog):
|
|
12
|
+
def __init__(self, parent=None):
|
|
13
|
+
# Setting up the widgets and layout
|
|
14
|
+
super(MapWindow, self).__init__(parent)
|
|
15
|
+
self.setWindowTitle('Tiler map')
|
|
16
|
+
self.setGeometry(0, 0, 1200, 800)
|
|
17
|
+
|
|
18
|
+
# delete dialog when close
|
|
19
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
20
|
+
|
|
21
|
+
# Add OK/cancel buttons
|
|
22
|
+
self.ok_btn_box = QDialogButtonBox(Qt.Vertical)
|
|
23
|
+
self.ok_btn_box.addButton("Run Tiler", QDialogButtonBox.AcceptRole)
|
|
24
|
+
self.ok_btn_box.addButton("Cancel", QDialogButtonBox.RejectRole)
|
|
25
|
+
self.ok_btn_box.addButton("Help", QDialogButtonBox.HelpRole)
|
|
26
|
+
|
|
27
|
+
self.ok_btn_box.buttons()[0].setFixedSize(120, 40)
|
|
28
|
+
self.ok_btn_box.buttons()[1].setFixedSize(120, 40)
|
|
29
|
+
self.ok_btn_box.buttons()[2].setFixedSize(120, 40)
|
|
30
|
+
|
|
31
|
+
self.ok_btn_box.accepted.connect(self.run)
|
|
32
|
+
self.ok_btn_box.rejected.connect(self.cancel)
|
|
33
|
+
self.ok_btn_box.helpRequested.connect(self.help)
|
|
34
|
+
|
|
35
|
+
self.info_layout = QVBoxLayout() # layout reserved for tiles info widgets
|
|
36
|
+
self.vbox_group = QVBoxLayout()
|
|
37
|
+
self.vbox_group.addLayout(self.info_layout)
|
|
38
|
+
self.vbox_group.addStretch()
|
|
39
|
+
self.vbox_group.addWidget(self.ok_btn_box, alignment=Qt.AlignCenter)
|
|
40
|
+
|
|
41
|
+
groupbox_info = QGroupBox('Tiles')
|
|
42
|
+
groupbox_info.setLayout(self.vbox_group)
|
|
43
|
+
|
|
44
|
+
central_widget = QWidget()
|
|
45
|
+
map_layout = QHBoxLayout(central_widget)
|
|
46
|
+
map_layout.addWidget(groupbox_info)
|
|
47
|
+
|
|
48
|
+
groupbox_map = QGroupBox('Map')
|
|
49
|
+
self.map_widget = MapWidget()
|
|
50
|
+
self.map_widget.setContentsMargins(10, 10, 10, 10)
|
|
51
|
+
self.vbox_map = QVBoxLayout()
|
|
52
|
+
self.vbox_map.addWidget(self.map_widget)
|
|
53
|
+
groupbox_map.setLayout(self.vbox_map)
|
|
54
|
+
map_layout.addWidget(groupbox_map, 10)
|
|
55
|
+
self.setLayout(map_layout)
|
|
56
|
+
|
|
57
|
+
# Working with the maps with pyqtlet
|
|
58
|
+
self.map = L.map(self.map_widget)
|
|
59
|
+
self.map.setView([0, 0], 10) # this is necessary
|
|
60
|
+
|
|
61
|
+
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
|
|
62
|
+
|
|
63
|
+
# add marker layer
|
|
64
|
+
# self.add_marker_layer()
|
|
65
|
+
self.show()
|
|
66
|
+
|
|
67
|
+
def add_polygons_to_map(self, layer_name, polygons, color):
|
|
68
|
+
style = {'fillOpacity': 0.1, 'color': color}
|
|
69
|
+
vars()[layer_name] = L.polygon(polygons, style)
|
|
70
|
+
self.map.addLayer(vars()[layer_name])
|
|
71
|
+
|
|
72
|
+
# this works too. addLayer has to be called first
|
|
73
|
+
# self.map.runJavaScript("var stylePoly = {fillColor:'red',color: 'blue',weight:2,fillOpacity:0.8};", 0)
|
|
74
|
+
# self.map.runJavaScript(f'{self.multipolygon.jsName}.setStyle(stylePoly);', 0)
|
|
75
|
+
|
|
76
|
+
def add_polylines_to_map(self, polylines, color):
|
|
77
|
+
style = {'color': color}
|
|
78
|
+
lines = L.polyline(polylines, style)
|
|
79
|
+
self.map.addLayer(lines)
|
|
80
|
+
|
|
81
|
+
def set_view(self, point, zoom):
|
|
82
|
+
self.map = self.map.setView(point, zoom)
|
|
83
|
+
|
|
84
|
+
# bounds is a pair of corner points, LL and UR
|
|
85
|
+
def fit_bounds(self, bounds):
|
|
86
|
+
# self.map.fitBounds(bounds)
|
|
87
|
+
self.map.runJavaScript(f'{self.map.jsName}.fitBounds(bounds);', 0)
|
|
88
|
+
|
|
89
|
+
def add_marker_layer(self):
|
|
90
|
+
self.marker = L.marker([12.934056, -77.610029])
|
|
91
|
+
self.marker.bindPopup('Maps are a treasure.')
|
|
92
|
+
self.map.addLayer(self.marker)
|
|
93
|
+
|
|
94
|
+
# Create a icon called markerIcon in the js runtime.
|
|
95
|
+
self.map.runJavaScript('var markerIcon '
|
|
96
|
+
'= L.icon({iconUrl: "https://leafletjs.com/examples/custom-icons/leaf-red.png"});', 0)
|
|
97
|
+
|
|
98
|
+
# Edit the existing python object by accessing it's jsName property
|
|
99
|
+
self.map.runJavaScript(f'{self.marker.jsName}.setIcon(markerIcon);', 0)
|
|
100
|
+
|
|
101
|
+
def accept(self):
|
|
102
|
+
print("Run the tiling.")
|
|
103
|
+
QDialog.accept(self)
|
|
104
|
+
|
|
105
|
+
def run(self):
|
|
106
|
+
self.accept()
|
|
107
|
+
|
|
108
|
+
def cancel(self):
|
|
109
|
+
print("Tiling canceled.")
|
|
110
|
+
self.reject()
|
|
111
|
+
|
|
112
|
+
def help(self):
|
|
113
|
+
print("Help requested.")
|
|
114
|
+
|
|
115
|
+
def set_tiles_info(self, tiles_info):
|
|
116
|
+
tree = QTreeWidget()
|
|
117
|
+
tree.setColumnCount(2)
|
|
118
|
+
tree.setHeaderLabels(["Item", "Value"])
|
|
119
|
+
item = QTreeWidgetItem(['Tiles'])
|
|
120
|
+
for key, value in tiles_info.items():
|
|
121
|
+
child = QTreeWidgetItem([key, str(value)])
|
|
122
|
+
item.addChild(child)
|
|
123
|
+
|
|
124
|
+
tree.insertTopLevelItem(0, item)
|
|
125
|
+
tree.expandAll()
|
|
126
|
+
|
|
127
|
+
# add to group widget
|
|
128
|
+
self.vbox_group.insertWidget(0, tree)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == '__main__':
|
|
132
|
+
# supress web engine logging
|
|
133
|
+
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-logging --log-level=3"
|
|
134
|
+
|
|
135
|
+
app = QApplication(sys.argv)
|
|
136
|
+
widget = MapWindow()
|
|
137
|
+
|
|
138
|
+
# add polygons to map
|
|
139
|
+
polygon_coords_base = [[[17.285044, 78.286671], [16.606174, 80.748015], [17.886816, 83.518482]]]
|
|
140
|
+
widget.add_polygons_to_map(polygon_coords_base, 'blue')
|
|
141
|
+
|
|
142
|
+
polygon_coords = [[[17.385044, 78.486671], [16.506174, 80.648015], [17.686816, 83.218482]],
|
|
143
|
+
[[13.082680, 80.270718], [12.971599, 77.594563], [15.828126, 78.037279]]]
|
|
144
|
+
widget.add_polygons_to_map(polygon_coords, 'red')
|
|
145
|
+
|
|
146
|
+
sys.exit(app.exec_())
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from PyQt5.QtWidgets import (
|
|
4
|
+
QApplication,
|
|
5
|
+
QLineEdit,
|
|
6
|
+
QFileDialog,
|
|
7
|
+
QComboBox,
|
|
8
|
+
QWidget,
|
|
9
|
+
QPushButton,
|
|
10
|
+
QLabel,
|
|
11
|
+
QSlider,
|
|
12
|
+
QMessageBox,
|
|
13
|
+
QStyleOptionSlider,
|
|
14
|
+
QStyle,
|
|
15
|
+
QToolTip,
|
|
16
|
+
QAbstractSlider,
|
|
17
|
+
QHBoxLayout,
|
|
18
|
+
QVBoxLayout,
|
|
19
|
+
QSpinBox,
|
|
20
|
+
QDoubleSpinBox
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from PyQt5.QtCore import pyqtSignal, Qt, QPoint
|
|
24
|
+
from re import search
|
|
25
|
+
|
|
26
|
+
from common import *
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ToolWidgets(QWidget):
|
|
30
|
+
signal_save_tool_params = pyqtSignal(object)
|
|
31
|
+
|
|
32
|
+
def __init__(self, tool_name, tool_args, show_advanced, parent=None):
|
|
33
|
+
super(ToolWidgets, self).__init__(parent)
|
|
34
|
+
|
|
35
|
+
self.tool_name = tool_name
|
|
36
|
+
self.show_advanced = show_advanced
|
|
37
|
+
self.current_tool_api = ''
|
|
38
|
+
self.widget_list = []
|
|
39
|
+
self.setWindowTitle("Tool widgets")
|
|
40
|
+
|
|
41
|
+
self.create_widgets(tool_args)
|
|
42
|
+
layout = QVBoxLayout()
|
|
43
|
+
|
|
44
|
+
for item in self.widget_list:
|
|
45
|
+
layout.addWidget(item)
|
|
46
|
+
|
|
47
|
+
self.save_button = QPushButton('Save Parameters')
|
|
48
|
+
self.save_button.clicked.connect(self.save_tool_parameters)
|
|
49
|
+
self.save_button.setFixedSize(200, 50)
|
|
50
|
+
layout.addSpacing(20)
|
|
51
|
+
self.setLayout(layout)
|
|
52
|
+
|
|
53
|
+
def get_widgets_arguments(self):
|
|
54
|
+
args = {}
|
|
55
|
+
param_missing = False
|
|
56
|
+
for widget in self.widget_list:
|
|
57
|
+
v = widget.get_value()
|
|
58
|
+
if v and len(v) == 2:
|
|
59
|
+
args[v[0]] = v[1]
|
|
60
|
+
else:
|
|
61
|
+
print(f'[Missing argument]: {widget.name} parameter not specified.', 'missing')
|
|
62
|
+
param_missing = True
|
|
63
|
+
|
|
64
|
+
if param_missing:
|
|
65
|
+
args = None
|
|
66
|
+
|
|
67
|
+
return args
|
|
68
|
+
|
|
69
|
+
def create_widgets(self, tool_args):
|
|
70
|
+
param_num = 0
|
|
71
|
+
for p in tool_args:
|
|
72
|
+
json_str = json.dumps(p, sort_keys=True, indent=2, separators=(',', ': '))
|
|
73
|
+
pt = p['parameter_type']
|
|
74
|
+
widget = None
|
|
75
|
+
|
|
76
|
+
if 'ExistingFileOrFloat' in pt:
|
|
77
|
+
widget = FileOrFloat(json_str, None)
|
|
78
|
+
param_num = param_num + 1
|
|
79
|
+
elif 'ExistingFile' in pt or 'NewFile' in pt or 'Directory' in pt:
|
|
80
|
+
widget = FileSelector(json_str, None)
|
|
81
|
+
param_num = param_num + 1
|
|
82
|
+
elif 'FileList' in pt:
|
|
83
|
+
widget = MultiFileSelector(json_str, None)
|
|
84
|
+
param_num = param_num + 1
|
|
85
|
+
elif 'Boolean' in pt:
|
|
86
|
+
widget = BooleanInput(json_str)
|
|
87
|
+
param_num = param_num + 1
|
|
88
|
+
elif 'OptionList' in pt:
|
|
89
|
+
widget = OptionsInput(json_str)
|
|
90
|
+
param_num = param_num + 1
|
|
91
|
+
elif ('Float' in pt or 'Integer' in pt or
|
|
92
|
+
'Text' in pt or 'String' in pt or 'StringOrNumber' in pt or
|
|
93
|
+
'StringList' in pt or 'VectorAttributeField' in pt):
|
|
94
|
+
widget = DataInput(json_str)
|
|
95
|
+
param_num = param_num + 1
|
|
96
|
+
else:
|
|
97
|
+
msg_box = QMessageBox()
|
|
98
|
+
msg_box.setIcon(QMessageBox.Warning)
|
|
99
|
+
msg_box.setText("Unsupported parameter type: {}.".format(pt))
|
|
100
|
+
msg_box.exec()
|
|
101
|
+
|
|
102
|
+
param_value = None
|
|
103
|
+
if 'saved_value' in p.keys():
|
|
104
|
+
param_value = p['saved_value']
|
|
105
|
+
if param_value is None:
|
|
106
|
+
param_value = p['default_value']
|
|
107
|
+
if param_value is not None:
|
|
108
|
+
if type(widget) is OptionsInput:
|
|
109
|
+
widget.value = param_value
|
|
110
|
+
elif widget:
|
|
111
|
+
widget.value = param_value
|
|
112
|
+
else:
|
|
113
|
+
print('No default value found: {}'.format(p['name']))
|
|
114
|
+
|
|
115
|
+
# hide optional widgets
|
|
116
|
+
if widget:
|
|
117
|
+
if widget.optional and widget.label:
|
|
118
|
+
widget.label.setStyleSheet("QLabel { background-color : transparent; color : blue; }")
|
|
119
|
+
|
|
120
|
+
if not self.show_advanced and widget.optional:
|
|
121
|
+
widget.hide()
|
|
122
|
+
|
|
123
|
+
self.widget_list.append(widget)
|
|
124
|
+
|
|
125
|
+
def update_widgets(self, values_dict):
|
|
126
|
+
for key, value in values_dict.items():
|
|
127
|
+
for item in self.widget_list:
|
|
128
|
+
if key == item.flag:
|
|
129
|
+
item.set_value(value)
|
|
130
|
+
|
|
131
|
+
def save_tool_parameters(self):
|
|
132
|
+
params = {}
|
|
133
|
+
for item in self.widget_list:
|
|
134
|
+
if item.flag:
|
|
135
|
+
params[item.flag] = item.get_value()
|
|
136
|
+
|
|
137
|
+
self.signal_save_tool_params.emit(params)
|
|
138
|
+
|
|
139
|
+
def load_default_args(self):
|
|
140
|
+
for item in self.widget_list:
|
|
141
|
+
item.set_default_value()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class FileSelector(QWidget):
|
|
145
|
+
def __init__(self, json_str, parent=None):
|
|
146
|
+
super(FileSelector, self).__init__(parent)
|
|
147
|
+
|
|
148
|
+
# first make sure that the json data has the correct fields
|
|
149
|
+
params = json.loads(json_str)
|
|
150
|
+
self.name = params['name']
|
|
151
|
+
self.description = params['description']
|
|
152
|
+
self.flag = params['flag']
|
|
153
|
+
self.parameter_type = params['parameter_type']
|
|
154
|
+
self.file_type = ""
|
|
155
|
+
if "ExistingFile" in self.parameter_type:
|
|
156
|
+
self.file_type = params['parameter_type']['ExistingFile']
|
|
157
|
+
elif "NewFile" in self.parameter_type:
|
|
158
|
+
self.file_type = params['parameter_type']['NewFile']
|
|
159
|
+
self.optional = params['optional']
|
|
160
|
+
|
|
161
|
+
self.default_value = params['default_value']
|
|
162
|
+
self.value = self.default_value
|
|
163
|
+
if 'saved_value' in params.keys():
|
|
164
|
+
self.value = params['saved_value']
|
|
165
|
+
|
|
166
|
+
self.layout = QHBoxLayout()
|
|
167
|
+
self.label = QLabel(self.name)
|
|
168
|
+
self.label.setMinimumWidth(BT_LABEL_MIN_WIDTH)
|
|
169
|
+
self.in_file = QLineEdit(self.value)
|
|
170
|
+
self.btn_select = QPushButton("...")
|
|
171
|
+
self.btn_select.clicked.connect(self.select_file)
|
|
172
|
+
self.layout.addWidget(self.label)
|
|
173
|
+
self.layout.addWidget(self.in_file)
|
|
174
|
+
self.layout.addWidget(self.btn_select)
|
|
175
|
+
|
|
176
|
+
self.setLayout(self.layout)
|
|
177
|
+
|
|
178
|
+
def select_file(self):
|
|
179
|
+
try:
|
|
180
|
+
dialog = QFileDialog(self)
|
|
181
|
+
dialog.setViewMode(QFileDialog.Detail)
|
|
182
|
+
dialog.setDirectory(str(Path(self.value).parent))
|
|
183
|
+
dialog.selectFile(Path(self.value).name)
|
|
184
|
+
result = None
|
|
185
|
+
file_names = None
|
|
186
|
+
|
|
187
|
+
if "Directory" in self.parameter_type:
|
|
188
|
+
dialog.setFileMode(QFileDialog.Directory)
|
|
189
|
+
|
|
190
|
+
if dialog.exec_():
|
|
191
|
+
folder_name = dialog.selectedFiles()
|
|
192
|
+
print(folder_name)
|
|
193
|
+
self.set_value(folder_name)
|
|
194
|
+
elif "ExistingFile" in self.parameter_type or "NewFile" in self.parameter_type:
|
|
195
|
+
file_types = "All files '*.*')"
|
|
196
|
+
if 'RasterAndVector' in self.file_type:
|
|
197
|
+
file_types = """Shapefiles (*.shp);;
|
|
198
|
+
Raster files (*.dep *.tif *.tiff *.bil *.flt *.sdat *.rdc *.asc *grd)"""
|
|
199
|
+
elif 'Raster' in self.file_type:
|
|
200
|
+
file_types = """Tiff raster files (*.tif *.tiff);;
|
|
201
|
+
Other raster files (*.dep *.bil *.flt *.sdat *.rdc *.asc *.grd)"""
|
|
202
|
+
elif 'Lidar' in self.file_type:
|
|
203
|
+
file_types = "LiDAR files (*.las *.zlidar *.laz *.zip)"
|
|
204
|
+
elif 'Vector' in self.file_type:
|
|
205
|
+
file_types = "Shapefiles (*.shp)"
|
|
206
|
+
elif 'Text' in self.file_type:
|
|
207
|
+
file_types = "Text files (*.txt);; all files (*.*)"
|
|
208
|
+
elif 'Csv' in self.file_type:
|
|
209
|
+
file_types = "CSC files (*.csv);; all files (*.*)"
|
|
210
|
+
elif 'Dat' in self.file_type:
|
|
211
|
+
file_types = "Binary data files (*.dat);; all files (*.*)"
|
|
212
|
+
elif 'Html' in self.file_type:
|
|
213
|
+
file_types = "HTML files (*.html)"
|
|
214
|
+
elif 'json' in self.file_type or 'JSON' in self.file_type:
|
|
215
|
+
file_types = "JSON files (*.json)"
|
|
216
|
+
|
|
217
|
+
dialog.setNameFilter(file_types)
|
|
218
|
+
|
|
219
|
+
if "ExistingFile" in self.parameter_type:
|
|
220
|
+
dialog.setFileMode(QFileDialog.FileMode.ExistingFiles)
|
|
221
|
+
else:
|
|
222
|
+
dialog.setFileMode(QFileDialog.FileMode.AnyFile)
|
|
223
|
+
|
|
224
|
+
if dialog.exec_():
|
|
225
|
+
file_names = dialog.selectedFiles()
|
|
226
|
+
|
|
227
|
+
if not file_names:
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
if len(file_names) == 0:
|
|
231
|
+
print('No file(s) selected.')
|
|
232
|
+
|
|
233
|
+
if file_names[0] == '':
|
|
234
|
+
print('File name not valid.')
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
# append suffix when not
|
|
238
|
+
# TODO: more consideration for multiple formats
|
|
239
|
+
result = file_names[0]
|
|
240
|
+
file_path = Path(result)
|
|
241
|
+
if result != '':
|
|
242
|
+
break_loop = False
|
|
243
|
+
selected_filters = self.get_file_filter_list(dialog.selectedNameFilter())
|
|
244
|
+
|
|
245
|
+
if file_path.suffix not in selected_filters:
|
|
246
|
+
if selected_filters[0] != '.*':
|
|
247
|
+
file_path = file_path.with_suffix(selected_filters[0])
|
|
248
|
+
|
|
249
|
+
result = file_path.as_posix()
|
|
250
|
+
self.set_value(result)
|
|
251
|
+
except Exception as e:
|
|
252
|
+
print(e)
|
|
253
|
+
|
|
254
|
+
t = "file"
|
|
255
|
+
if self.parameter_type == "Directory":
|
|
256
|
+
t = "directory"
|
|
257
|
+
|
|
258
|
+
msg_box = QMessageBox()
|
|
259
|
+
msg_box.setIcon(QMessageBox.Warning)
|
|
260
|
+
msg_box.setText("Could not find {}".format(t))
|
|
261
|
+
msg_box.exec()
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def get_file_filter_list(filter_str):
|
|
265
|
+
"""
|
|
266
|
+
Extract filters out of full filter string, split int list and replace first '*'
|
|
267
|
+
Result: ['.shp', '.*']
|
|
268
|
+
"""
|
|
269
|
+
filter_list = search('\((.+?)\)', filter_str).group(1).split(' ')
|
|
270
|
+
filter_list = [item.replace('*', '', 1) for item in filter_list if item != '']
|
|
271
|
+
return filter_list
|
|
272
|
+
|
|
273
|
+
def get_value(self):
|
|
274
|
+
return self.flag, self.value
|
|
275
|
+
|
|
276
|
+
def set_value(self, value):
|
|
277
|
+
if type(value) is list:
|
|
278
|
+
if len(value) > 0:
|
|
279
|
+
value = value[0]
|
|
280
|
+
|
|
281
|
+
self.value = value
|
|
282
|
+
self.in_file.setText(self.value)
|
|
283
|
+
self.in_file.setToolTip(self.value)
|
|
284
|
+
|
|
285
|
+
def set_default_value(self):
|
|
286
|
+
self.value = self.default_value
|
|
287
|
+
self.in_file.setText(self.value)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class FileOrFloat(QWidget):
|
|
291
|
+
def __init__(self, json_str, parent=None):
|
|
292
|
+
super(FileOrFloat, self).__init__(parent)
|
|
293
|
+
pass
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class MultiFileSelector(QWidget):
|
|
297
|
+
def __init__(self, json_str, parent=None):
|
|
298
|
+
super(MultiFileSelector, self).__init__(parent)
|
|
299
|
+
pass
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class BooleanInput(QWidget):
|
|
303
|
+
def __init__(self, json_str, parent=None):
|
|
304
|
+
super(BooleanInput, self).__init__(parent)
|
|
305
|
+
pass
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class OptionsInput(QWidget):
|
|
309
|
+
def __init__(self, json_str, parent=None):
|
|
310
|
+
super(OptionsInput, self).__init__(parent)
|
|
311
|
+
|
|
312
|
+
# first make sure that the json data has the correct fields
|
|
313
|
+
params = json.loads(json_str)
|
|
314
|
+
self.name = params['name']
|
|
315
|
+
self.description = params['description']
|
|
316
|
+
self.flag = params['flag']
|
|
317
|
+
self.parameter_type = params['parameter_type']
|
|
318
|
+
self.optional = params['optional']
|
|
319
|
+
self.data_type = params['data_type']
|
|
320
|
+
|
|
321
|
+
self.default_value = str(params['default_value'])
|
|
322
|
+
self.value = self.default_value
|
|
323
|
+
if 'saved_value' in params.keys():
|
|
324
|
+
self.value = params['saved_value']
|
|
325
|
+
|
|
326
|
+
self.label = QLabel(self.name)
|
|
327
|
+
self.label.setMinimumWidth(BT_LABEL_MIN_WIDTH)
|
|
328
|
+
self.combobox = QComboBox()
|
|
329
|
+
self.combobox.currentIndexChanged.connect(self.selection_change)
|
|
330
|
+
|
|
331
|
+
i = 1
|
|
332
|
+
default_index = -1
|
|
333
|
+
self.option_list = params['parameter_type']['OptionList']
|
|
334
|
+
if self.option_list:
|
|
335
|
+
self.option_list = [str(item) for item in self.option_list] # convert to strings
|
|
336
|
+
values = ()
|
|
337
|
+
for v in self.option_list:
|
|
338
|
+
values += (v,)
|
|
339
|
+
if v == str(self.value):
|
|
340
|
+
default_index = i - 1
|
|
341
|
+
i = i + 1
|
|
342
|
+
|
|
343
|
+
self.combobox.addItems(self.option_list)
|
|
344
|
+
self.combobox.setCurrentIndex(default_index)
|
|
345
|
+
|
|
346
|
+
self.layout = QHBoxLayout()
|
|
347
|
+
self.layout.addWidget(self.label)
|
|
348
|
+
self.layout.addWidget(self.combobox)
|
|
349
|
+
self.setLayout(self.layout)
|
|
350
|
+
|
|
351
|
+
def selection_change(self, i):
|
|
352
|
+
self.value = self.option_list[i]
|
|
353
|
+
|
|
354
|
+
def set_value(self, value):
|
|
355
|
+
self.value = value
|
|
356
|
+
for v in self.option_list:
|
|
357
|
+
if value == v:
|
|
358
|
+
self.combobox.setCurrentIndex(self.option_list.index(v))
|
|
359
|
+
|
|
360
|
+
def set_default_value(self):
|
|
361
|
+
self.value = self.default_value
|
|
362
|
+
for v in self.option_list:
|
|
363
|
+
if self.value == v:
|
|
364
|
+
self.combobox.setCurrentIndex(self.option_list.index(v))
|
|
365
|
+
|
|
366
|
+
def get_value(self):
|
|
367
|
+
return self.flag, self.value
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
class DataInput(QWidget):
|
|
371
|
+
def __init__(self, json_str, parent=None):
|
|
372
|
+
super(DataInput, self).__init__(parent)
|
|
373
|
+
|
|
374
|
+
# first make sure that the json data has the correct fields
|
|
375
|
+
params = json.loads(json_str)
|
|
376
|
+
self.name = params['name']
|
|
377
|
+
self.description = params['description']
|
|
378
|
+
self.flag = params['flag']
|
|
379
|
+
self.parameter_type = params['parameter_type']
|
|
380
|
+
self.optional = params['optional']
|
|
381
|
+
|
|
382
|
+
self.default_value = params['default_value']
|
|
383
|
+
self.value = self.default_value
|
|
384
|
+
if 'saved_value' in params.keys():
|
|
385
|
+
self.value = params['saved_value']
|
|
386
|
+
|
|
387
|
+
self.label = QLabel(self.name)
|
|
388
|
+
self.label.setMinimumWidth(BT_LABEL_MIN_WIDTH)
|
|
389
|
+
self.data_input = None
|
|
390
|
+
|
|
391
|
+
if "Integer" in self.parameter_type:
|
|
392
|
+
self.data_input = QSpinBox()
|
|
393
|
+
elif "Float" in self.parameter_type or "Double" in self.parameter_type:
|
|
394
|
+
self.data_input = QDoubleSpinBox()
|
|
395
|
+
|
|
396
|
+
if self.data_input:
|
|
397
|
+
self.data_input.setValue(self.value)
|
|
398
|
+
|
|
399
|
+
self.data_input.valueChanged.connect(self.update_value)
|
|
400
|
+
|
|
401
|
+
self.layout = QHBoxLayout()
|
|
402
|
+
self.layout.addWidget(self.label)
|
|
403
|
+
self.layout.addWidget(self.data_input)
|
|
404
|
+
self.setLayout(self.layout)
|
|
405
|
+
|
|
406
|
+
def update_value(self):
|
|
407
|
+
self.value = self.data_input.value()
|
|
408
|
+
|
|
409
|
+
def get_value(self):
|
|
410
|
+
v = self.value
|
|
411
|
+
if v is not None:
|
|
412
|
+
if "Integer" in self.parameter_type:
|
|
413
|
+
return self.flag, int(self.value)
|
|
414
|
+
elif "Float" in self.parameter_type:
|
|
415
|
+
return self.flag, float(self.value)
|
|
416
|
+
elif "Double" in self.parameter_type:
|
|
417
|
+
return self.flag, float(self.value)
|
|
418
|
+
else: # String or StringOrNumber types
|
|
419
|
+
return self.flag, self.value
|
|
420
|
+
else:
|
|
421
|
+
if not self.optional:
|
|
422
|
+
msg_box = QMessageBox()
|
|
423
|
+
msg_box.setIcon(QMessageBox.Warning)
|
|
424
|
+
msg_box.setText("Unspecified non-optional parameter {}.".format(self.flag))
|
|
425
|
+
msg_box.exec()
|
|
426
|
+
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
def set_value(self, value):
|
|
430
|
+
if self.data_input:
|
|
431
|
+
self.data_input.setValue(value)
|
|
432
|
+
self.update_value()
|
|
433
|
+
|
|
434
|
+
def set_default_value(self):
|
|
435
|
+
if self.data_input:
|
|
436
|
+
self.data_input.setValue(self.default_value)
|
|
437
|
+
self.update_value()
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class DoubleSlider(QSlider):
|
|
441
|
+
# create our signal that we can connect to if necessary
|
|
442
|
+
doubleValueChanged = pyqtSignal(float)
|
|
443
|
+
|
|
444
|
+
def __init__(self, decimals=3, *args, **kargs):
|
|
445
|
+
super(DoubleSlider, self).__init__(Qt.Horizontal)
|
|
446
|
+
self._multi = 10 ** decimals
|
|
447
|
+
|
|
448
|
+
self.opt = QStyleOptionSlider()
|
|
449
|
+
self.initStyleOption(self.opt)
|
|
450
|
+
|
|
451
|
+
self.valueChanged.connect(self.emit_double_value_changed)
|
|
452
|
+
|
|
453
|
+
def emit_double_value_changed(self):
|
|
454
|
+
value = float(super(DoubleSlider, self).value()) / self._multi
|
|
455
|
+
self.doubleValueChanged.emit(value)
|
|
456
|
+
|
|
457
|
+
def value(self):
|
|
458
|
+
return float(super(DoubleSlider, self).value()) / self._multi
|
|
459
|
+
|
|
460
|
+
def setMinimum(self, value):
|
|
461
|
+
return super(DoubleSlider, self).setMinimum(value * self._multi)
|
|
462
|
+
|
|
463
|
+
def setMaximum(self, value):
|
|
464
|
+
return super(DoubleSlider, self).setMaximum(value * self._multi)
|
|
465
|
+
|
|
466
|
+
def setSingleStep(self, value):
|
|
467
|
+
return super(DoubleSlider, self).setSingleStep(value * self._multi)
|
|
468
|
+
|
|
469
|
+
def singleStep(self):
|
|
470
|
+
return float(super(DoubleSlider, self).singleStep()) / self._multi
|
|
471
|
+
|
|
472
|
+
def setValue(self, value):
|
|
473
|
+
super(DoubleSlider, self).setValue(int(value * self._multi))
|
|
474
|
+
|
|
475
|
+
def sliderChange(self, change):
|
|
476
|
+
if change == QAbstractSlider.SliderValueChange:
|
|
477
|
+
sr = self.style().subControlRect(QStyle.CC_Slider, self.opt, QStyle.SC_SliderHandle)
|
|
478
|
+
bottom_right_corner = sr.bottomLeft()
|
|
479
|
+
QToolTip.showText(self.mapToGlobal(QPoint(bottom_right_corner.x(), bottom_right_corner.y())),
|
|
480
|
+
str(self.value()), self)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
if __name__ == '__main__':
|
|
484
|
+
from bt_data import BTData
|
|
485
|
+
|
|
486
|
+
bt = BTData()
|
|
487
|
+
|
|
488
|
+
app = QApplication(sys.argv)
|
|
489
|
+
dlg = ToolWidgets('Raster Line Attributes',
|
|
490
|
+
bt.get_bera_tool_args('Raster Line Attributes'),
|
|
491
|
+
bt.show_advanced)
|
|
492
|
+
dlg.show()
|
|
493
|
+
sys.exit(app.exec_())
|