349 lines
15 KiB
Python
349 lines
15 KiB
Python
import inspect
|
|
import os
|
|
from typing import Any, List, Tuple, Union
|
|
from speckle.converter.layers import getAllLayers
|
|
from speckle.converter.layers.utils import getElevationLayer, getLayerGeomType
|
|
from specklepy_qt_ui.logger import displayUserMsg, logToUser
|
|
|
|
from qgis.core import QgsVectorLayer, QgsRasterLayer, QgsIconUtils
|
|
|
|
from PyQt5 import QtWidgets, uic, QtCore
|
|
from PyQt5.QtWidgets import QListWidgetItem
|
|
|
|
from specklepy.logging import metrics
|
|
from osgeo import gdal
|
|
import webbrowser
|
|
|
|
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
|
|
FORM_CLASS, _ = uic.loadUiType(
|
|
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "transforms.ui") )
|
|
)
|
|
|
|
class MappingSendDialog(QtWidgets.QWidget, FORM_CLASS):
|
|
|
|
dialog_button_box: QtWidgets.QPushButton = None
|
|
more_info: QtWidgets.QPushButton = None
|
|
layerDropdown: QtWidgets.QComboBox
|
|
transformDropdown: QtWidgets.QComboBox
|
|
addTransform: QtWidgets.QPushButton
|
|
removeTransform: QtWidgets.QPushButton
|
|
transformationsList: QtWidgets.QListWidget
|
|
elevationLayerDropdown: QtWidgets.QComboBox
|
|
|
|
attrDropdown: QtWidgets.QComboBox
|
|
dataStorage: Any = None
|
|
|
|
def __init__(self, parent=None):
|
|
super(MappingSendDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
|
|
self.setupUi(self)
|
|
self.setMinimumWidth(600)
|
|
|
|
self.addTransform.setStyleSheet("QPushButton {color: black; padding:3px;padding-left:5px;border: none; } QPushButton:hover { background-color: lightgrey}")
|
|
self.removeTransform.setStyleSheet("QPushButton {color: black; padding:3px;padding-left:5px;border: none; } QPushButton:hover { background-color: lightgrey}")
|
|
|
|
#self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
|
|
self.addTransform.clicked.connect(self.onAddTransform)
|
|
self.removeTransform.clicked.connect(self.onRemoveTransform)
|
|
self.transformDropdown.currentIndexChanged.connect(self.populateLayersByTransform)
|
|
self.transformDropdown.currentIndexChanged.connect(self.populateAttributesByLayer)
|
|
self.layerDropdown.currentIndexChanged.connect(self.populateAttributesByLayer)
|
|
self.dialog_button_box.clicked.connect(self.saveElevationLayer)
|
|
self.dialog_button_box.clicked.connect(self.onOkClicked)
|
|
self.more_info.clicked.connect(self.onMoreInfo)
|
|
|
|
return
|
|
|
|
|
|
def runSetup(self):
|
|
|
|
self.attr_label.setEnabled(False)
|
|
self.attrDropdown.setEnabled(False)
|
|
|
|
self.populateTransforms()
|
|
self.populateLayersByTransform()
|
|
self.populateSavedTransforms()
|
|
self.populateSavedElevationLayer()
|
|
|
|
def populateSavedTransforms(self, dataStorage = None): #, savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
|
|
|
|
if dataStorage is not None:
|
|
self.dataStorage = dataStorage # making sure lists are synced
|
|
self.transformationsList.clear()
|
|
vals = self.dataStorage.savedTransforms
|
|
all_l_names = [l.name() for l in self.dataStorage.all_layers]
|
|
|
|
for item in vals:
|
|
layer_name = item.split(" -> ")[0].split(" (\'")[0]
|
|
transform_name = item.split(" -> ")[1]
|
|
|
|
layer = None
|
|
for l in self.dataStorage.all_layers:
|
|
if layer_name == l.name():
|
|
layer = l
|
|
if layer is None:
|
|
displayUserMsg(f"Layer \'{layer_name}\' not found in the project.\nTransformation is removed.", level=2)
|
|
self.dataStorage.savedTransforms.remove(item)
|
|
else:
|
|
if transform_name not in self.dataStorage.transformsCatalog:
|
|
displayUserMsg(f"Saved transformation \'{transform_name}\' is not valid.\n. Transformation is removed.", level=1)
|
|
self.dataStorage.savedTransforms.remove(item)
|
|
elif all_l_names.count(layer.name()) > 1:
|
|
displayUserMsg(f"Layer name \'{layer.name()}\' is used for more than 1 layer in the project", level=1)
|
|
self.dataStorage.savedTransforms.remove(item)
|
|
else:
|
|
listItem = QListWidgetItem(item)
|
|
icon = QgsIconUtils().iconForLayer(layer)
|
|
listItem.setIcon(icon)
|
|
|
|
self.transformationsList.addItem(listItem)
|
|
|
|
def onAddTransform(self):
|
|
|
|
from speckle.utils.project_vars import set_transformations
|
|
root = self.dataStorage.project.layerTreeRoot()
|
|
self.dataStorage.all_layers = getAllLayers(root)
|
|
|
|
if len(self.layerDropdown.currentText())>1 and len(self.transformDropdown.currentText())>1:
|
|
listItem = str(self.layerDropdown.currentText()) + " -> " + str(self.transformDropdown.currentText())
|
|
layer_name = listItem.split(" -> ")[0].split(" (\'")[0]
|
|
transform_name = listItem.split(" -> ")[1].lower()
|
|
|
|
exists = 0
|
|
for record in self.dataStorage.savedTransforms:
|
|
current_layer_name = record.split(" -> ")[0].split(" (\'")[0]
|
|
current_transf_name = record.split(" -> ")[1].lower()
|
|
if layer_name == current_layer_name: # in layers
|
|
exists +=1
|
|
displayUserMsg("Selected layer already has a transformation applied", level=1)
|
|
break
|
|
if ("elevation" in transform_name and "mesh" in transform_name and "texture" not in transform_name) and transform_name == current_transf_name: # in transforms
|
|
exists +=1
|
|
displayUserMsg(f"Layer '{current_layer_name}' is already assigned as a 3d elevation", level=1)
|
|
break
|
|
if exists == 0:
|
|
layer = None
|
|
for l in self.dataStorage.all_layers:
|
|
if layer_name == l.name():
|
|
layer = l
|
|
if layer is not None:
|
|
|
|
if "attribute" in transform_name and self.attrDropdown.currentText() != '':
|
|
listItem = str(self.layerDropdown.currentText()) + " (\'" + str(self.attrDropdown.currentText()) + "\') -> " + str(self.transformDropdown.currentText())
|
|
|
|
self.dataStorage.savedTransforms.append(listItem)
|
|
self.populateSavedTransforms()
|
|
|
|
try:
|
|
metrics.track("Connector Action", self.dataStorage.active_account, {"name": "Add transformation on Send", "Transformation": listItem.split(" -> ")[1], "connector_version": str(self.dataStorage.plugin_version)})
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3] )
|
|
|
|
set_transformations(self.dataStorage)
|
|
|
|
def onRemoveTransform(self):
|
|
|
|
from speckle.utils.project_vars import set_transformations
|
|
if self.transformationsList.currentItem() is not None:
|
|
listItem = self.transformationsList.currentItem().text()
|
|
print(listItem)
|
|
|
|
if listItem in self.dataStorage.savedTransforms:
|
|
self.dataStorage.savedTransforms.remove(listItem)
|
|
|
|
self.populateSavedTransforms()
|
|
set_transformations(self.dataStorage)
|
|
|
|
def onOkClicked(self):
|
|
try:
|
|
self.close()
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
def populateLayers(self):
|
|
try:
|
|
self.layerDropdown.clear()
|
|
root = self.dataStorage.project.layerTreeRoot()
|
|
self.dataStorage.all_layers = getAllLayers(root)
|
|
for i, layer in enumerate(self.dataStorage.all_layers):
|
|
listItem = layer.name()
|
|
self.layerDropdown.addItem(listItem)
|
|
icon = QgsIconUtils().iconForLayer(layer)
|
|
self.layerDropdown.setItemIcon(i, icon)
|
|
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
def populateLayersByTransform(self):
|
|
try:
|
|
self.layerDropdown.clear()
|
|
root = self.dataStorage.project.layerTreeRoot()
|
|
self.dataStorage.all_layers = getAllLayers(root)
|
|
|
|
transform = str(self.transformDropdown.currentText())
|
|
layers_dropdown = []
|
|
|
|
for i, layer in enumerate(self.dataStorage.all_layers):
|
|
|
|
listItem = None
|
|
if "extrude" in transform.lower():
|
|
|
|
if isinstance(layer, QgsVectorLayer):
|
|
geom_type = getLayerGeomType(layer)
|
|
if "polygon" in geom_type.lower():
|
|
listItem = layer.name()
|
|
|
|
elif "elevation" in transform.lower():
|
|
if isinstance(layer, QgsRasterLayer):
|
|
# avoiding tiling layers
|
|
ds = gdal.Open(layer.source(), gdal.GA_ReadOnly)
|
|
if ds is None:
|
|
continue
|
|
|
|
listItem = layer.name()
|
|
|
|
if listItem is not None:
|
|
layers_dropdown.append(listItem)
|
|
self.layerDropdown.addItem(listItem)
|
|
icon = QgsIconUtils().iconForLayer(layer)
|
|
self.layerDropdown.setItemIcon(len(layers_dropdown)-1, icon)
|
|
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
def populateAttributesByLayer(self):
|
|
try:
|
|
self.attrDropdown.clear()
|
|
root = self.dataStorage.project.layerTreeRoot()
|
|
self.dataStorage.all_layers = getAllLayers(root)
|
|
|
|
layer_name = str(self.layerDropdown.currentText())
|
|
transform_name = self.transformDropdown.currentText()
|
|
layerForAttributes = None
|
|
for i, layer in enumerate(self.dataStorage.all_layers):
|
|
|
|
if layer_name == layer.name():
|
|
if isinstance(layer, QgsVectorLayer):
|
|
geom_type = getLayerGeomType(layer)
|
|
if "polygon" in geom_type.lower():
|
|
layerForAttributes = layer
|
|
break
|
|
|
|
if layerForAttributes is not None and 'attribute' in transform_name:
|
|
self.attr_label.setEnabled(True)
|
|
self.attrDropdown.setEnabled(True)
|
|
|
|
if 'ignore' not in transform_name:
|
|
self.attrDropdown.addItem('Random height')
|
|
|
|
for field in layerForAttributes.fields():
|
|
field_type = field.type()
|
|
if field_type in [2,6,10]:
|
|
self.attrDropdown.addItem(str(field.name()))
|
|
else:
|
|
self.attr_label.setEnabled(False)
|
|
self.attrDropdown.setEnabled(False)
|
|
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
def populateTransforms(self):
|
|
try:
|
|
self.transformDropdown.clear()
|
|
for item in self.dataStorage.transformsCatalog:
|
|
self.transformDropdown.addItem(item)
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
def nameCheck(self):
|
|
return
|
|
try:
|
|
if len(self.name_field.text()) == 0 or len(self.name_field.text()) >= 3:
|
|
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
|
|
else:
|
|
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
|
|
return
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
|
|
def populateSavedElevationLayer(self, dataStorage = None): #, savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
|
|
|
|
try:
|
|
if dataStorage is not None:
|
|
self.dataStorage = dataStorage # making sure lists are synced
|
|
elevationLayer = getElevationLayer(self.dataStorage)
|
|
|
|
self.elevationLayerDropdown.clear()
|
|
root = self.dataStorage.project.layerTreeRoot()
|
|
self.dataStorage.all_layers = getAllLayers(root)
|
|
|
|
self.elevationLayerDropdown.addItem("")
|
|
|
|
setAsindex = 0
|
|
countRaster = 1
|
|
for i, layer in enumerate(self.dataStorage.all_layers):
|
|
if isinstance(layer, QgsRasterLayer):
|
|
# avoiding tiling layers
|
|
ds = gdal.Open(layer.source(), gdal.GA_ReadOnly)
|
|
if ds is None:
|
|
continue
|
|
|
|
listItem = layer.name()
|
|
self.elevationLayerDropdown.addItem(listItem)
|
|
icon = QgsIconUtils().iconForLayer(layer)
|
|
self.elevationLayerDropdown.setItemIcon(countRaster, icon)
|
|
|
|
if elevationLayer is not None:
|
|
if listItem == elevationLayer.name():
|
|
setAsindex = countRaster
|
|
countRaster += 1
|
|
self.elevationLayerDropdown.setCurrentIndex(setAsindex)
|
|
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
|
return
|
|
|
|
def saveElevationLayer(self):
|
|
from speckle.utils.project_vars import set_elevationLayer
|
|
root = self.dataStorage.project.layerTreeRoot()
|
|
layer = None
|
|
|
|
if self.dataStorage is None: return
|
|
|
|
layerName = str(self.elevationLayerDropdown.currentText())
|
|
if len(layerName) < 1:
|
|
layer = None
|
|
else:
|
|
self.dataStorage.all_layers = getAllLayers(root)
|
|
all_l_names = [l.name() for l in self.dataStorage.all_layers]
|
|
|
|
for l in self.dataStorage.all_layers:
|
|
if layerName == l.name():
|
|
layer = l
|
|
try:
|
|
if all_l_names.count(layer.name()) > 1:
|
|
displayUserMsg(f"Layer name \'{layer.name()}\' is used for more than 1 layer in the project", level=1)
|
|
layer = None
|
|
break
|
|
except:
|
|
displayUserMsg(f"Layer \'{layer.name()}\' is not found in the project", level=1)
|
|
layer = None
|
|
break
|
|
|
|
self.dataStorage.elevationLayer = layer
|
|
set_elevationLayer(self.dataStorage)
|
|
|
|
try:
|
|
metrics.track("Connector Action", self.dataStorage.active_account, {"name": "Add transformation on Send", "Transformation": "Set Layer as Elevation", "connector_version": str(self.dataStorage.plugin_version)})
|
|
except Exception as e:
|
|
logToUser(e, level = 2, func = inspect.stack()[0][3] )
|
|
|
|
def onMoreInfo(self):
|
|
webbrowser.open("https://speckle.guide/user/qgis.html#transformations")
|
|
|