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.
Files changed (142) hide show
  1. beratools/__init__.py +9 -0
  2. beratools/core/__init__.py +0 -0
  3. beratools/core/algo_centerline.py +351 -0
  4. beratools/core/constants.py +86 -0
  5. beratools/core/dijkstra_algorithm.py +460 -0
  6. beratools/core/logger.py +85 -0
  7. beratools/core/tool_base.py +133 -0
  8. beratools/gui/__init__.py +15 -0
  9. beratools/gui/batch_processing_dlg.py +463 -0
  10. beratools/gui/beratools.json +2300 -0
  11. beratools/gui/bt_data.py +487 -0
  12. beratools/gui/bt_gui_main.py +691 -0
  13. beratools/gui/cli.py +18 -0
  14. beratools/gui/gui.json +8 -0
  15. beratools/gui/img/BERALogo.png +0 -0
  16. beratools/gui/img/closed.gif +0 -0
  17. beratools/gui/img/closed.png +0 -0
  18. beratools/gui/img/open.gif +0 -0
  19. beratools/gui/img/open.png +0 -0
  20. beratools/gui/img/tool.gif +0 -0
  21. beratools/gui/img/tool.png +0 -0
  22. beratools/gui/map_window.py +146 -0
  23. beratools/gui/tool_widgets.py +493 -0
  24. beratools/gui_tk/ASCII Banners.txt +248 -0
  25. beratools/gui_tk/__init__.py +20 -0
  26. beratools/gui_tk/beratools_main.py +515 -0
  27. beratools/gui_tk/bt_widgets.py +442 -0
  28. beratools/gui_tk/cli.py +18 -0
  29. beratools/gui_tk/gui.json +8 -0
  30. beratools/gui_tk/img/BERALogo.png +0 -0
  31. beratools/gui_tk/img/closed.gif +0 -0
  32. beratools/gui_tk/img/closed.png +0 -0
  33. beratools/gui_tk/img/open.gif +0 -0
  34. beratools/gui_tk/img/open.png +0 -0
  35. beratools/gui_tk/img/tool.gif +0 -0
  36. beratools/gui_tk/img/tool.png +0 -0
  37. beratools/gui_tk/main.py +14 -0
  38. beratools/gui_tk/map_window.py +144 -0
  39. beratools/gui_tk/runner.py +1481 -0
  40. beratools/gui_tk/tooltip.py +55 -0
  41. beratools/third_party/pyqtlet2/__init__.py +9 -0
  42. beratools/third_party/pyqtlet2/leaflet/__init__.py +26 -0
  43. beratools/third_party/pyqtlet2/leaflet/control/__init__.py +6 -0
  44. beratools/third_party/pyqtlet2/leaflet/control/control.py +59 -0
  45. beratools/third_party/pyqtlet2/leaflet/control/draw.py +52 -0
  46. beratools/third_party/pyqtlet2/leaflet/control/layers.py +20 -0
  47. beratools/third_party/pyqtlet2/leaflet/core/Parser.py +24 -0
  48. beratools/third_party/pyqtlet2/leaflet/core/__init__.py +2 -0
  49. beratools/third_party/pyqtlet2/leaflet/core/evented.py +180 -0
  50. beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +5 -0
  51. beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +34 -0
  52. beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +1 -0
  53. beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +30 -0
  54. beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +18 -0
  55. beratools/third_party/pyqtlet2/leaflet/layer/layer.py +105 -0
  56. beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +45 -0
  57. beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +1 -0
  58. beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +91 -0
  59. beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +2 -0
  60. beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +4 -0
  61. beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +16 -0
  62. beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +5 -0
  63. beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +15 -0
  64. beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +18 -0
  65. beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +5 -0
  66. beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +14 -0
  67. beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +18 -0
  68. beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +14 -0
  69. beratools/third_party/pyqtlet2/leaflet/map/__init__.py +1 -0
  70. beratools/third_party/pyqtlet2/leaflet/map/map.py +220 -0
  71. beratools/third_party/pyqtlet2/mapwidget.py +45 -0
  72. beratools/third_party/pyqtlet2/web/custom.js +43 -0
  73. beratools/third_party/pyqtlet2/web/map.html +23 -0
  74. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
  75. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
  76. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
  77. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
  78. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
  79. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +656 -0
  80. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +6 -0
  81. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +14 -0
  82. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +4 -0
  83. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +22 -0
  84. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +43 -0
  85. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +20 -0
  86. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
  87. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
  88. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
  89. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
  90. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
  91. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
  92. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
  93. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +156 -0
  94. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +10 -0
  95. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +10 -0
  96. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +22 -0
  97. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +57 -0
  98. beratools/tools/Beratools_r_script.r +1120 -0
  99. beratools/tools/Ht_metrics.py +116 -0
  100. beratools/tools/__init__.py +7 -0
  101. beratools/tools/batch_processing.py +132 -0
  102. beratools/tools/canopy_threshold_relative.py +670 -0
  103. beratools/tools/canopycostraster.py +222 -0
  104. beratools/tools/centerline.py +176 -0
  105. beratools/tools/common.py +885 -0
  106. beratools/tools/fl_regen_csf.py +428 -0
  107. beratools/tools/forest_line_attributes.py +408 -0
  108. beratools/tools/forest_line_ecosite.py +216 -0
  109. beratools/tools/lapis_all.py +103 -0
  110. beratools/tools/least_cost_path_from_chm.py +152 -0
  111. beratools/tools/line_footprint_absolute.py +363 -0
  112. beratools/tools/line_footprint_fixed.py +282 -0
  113. beratools/tools/line_footprint_functions.py +720 -0
  114. beratools/tools/line_footprint_relative.py +64 -0
  115. beratools/tools/ln_relative_metrics.py +615 -0
  116. beratools/tools/r_cal_lpi_elai.r +25 -0
  117. beratools/tools/r_generate_pd_focalraster.r +101 -0
  118. beratools/tools/r_interface.py +80 -0
  119. beratools/tools/r_point_density.r +9 -0
  120. beratools/tools/rpy_chm2trees.py +86 -0
  121. beratools/tools/rpy_dsm_chm_by.py +81 -0
  122. beratools/tools/rpy_dtm_by.py +63 -0
  123. beratools/tools/rpy_find_cellsize.py +43 -0
  124. beratools/tools/rpy_gnd_csf.py +74 -0
  125. beratools/tools/rpy_hummock_hollow.py +85 -0
  126. beratools/tools/rpy_hummock_hollow_raster.py +71 -0
  127. beratools/tools/rpy_las_info.py +51 -0
  128. beratools/tools/rpy_laz2las.py +40 -0
  129. beratools/tools/rpy_lpi_elai_lascat.py +466 -0
  130. beratools/tools/rpy_normalized_lidar_by.py +56 -0
  131. beratools/tools/rpy_percent_above_dbh.py +80 -0
  132. beratools/tools/rpy_points2trees.py +88 -0
  133. beratools/tools/rpy_vegcoverage.py +94 -0
  134. beratools/tools/tiler.py +206 -0
  135. beratools/tools/tool_template.py +54 -0
  136. beratools/tools/vertex_optimization.py +620 -0
  137. beratools/tools/zonal_threshold.py +144 -0
  138. beratools-0.2.0.dist-info/METADATA +63 -0
  139. beratools-0.2.0.dist-info/RECORD +142 -0
  140. beratools-0.2.0.dist-info/WHEEL +4 -0
  141. beratools-0.2.0.dist-info/entry_points.txt +2 -0
  142. beratools-0.2.0.dist-info/licenses/LICENSE +22 -0
beratools/gui/gui.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "ascii_art": [
3
+ " ____ ____ ____ __ ____ _____ _____ __ ___ \n",
4
+ "( _ \\( ___)( _ \\ /__\\ (_ _)( _ )( _ )( ) / __) \n",
5
+ " ) _ < )__) ) / /(__)\\ )( )(_)( )(_)( )(__ \\__ \\ \n",
6
+ "(____/(____)(_)\\_)(__)(__) (__) (_____)(_____)(____)(___/ \n"
7
+ ]
8
+ }
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_())