class Ui_ScenePropertiesWidget(object): def setupUi(self, ScenePropertiesWidget): ScenePropertiesWidget.setObjectName("ScenePropertiesWidget") ScenePropertiesWidget.resize(385, 657) self.verticalLayout_3 = QtWidgets.QVBoxLayout(ScenePropertiesWidget) self.verticalLayout_3.setObjectName("verticalLayout_3") self.groupBox_1 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_1.setObjectName("groupBox_1") self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_1) self.gridLayout_4.setObjectName("gridLayout_4") self.lineEdit_BaseSize = QtWidgets.QLineEdit(self.groupBox_1) self.lineEdit_BaseSize.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_BaseSize.setObjectName("lineEdit_BaseSize") self.gridLayout_4.addWidget(self.lineEdit_BaseSize, 0, 1, 1, 2) self.label_2 = QtWidgets.QLabel(self.groupBox_1) self.label_2.setObjectName("label_2") self.gridLayout_4.addWidget(self.label_2, 2, 0, 1, 1) self.label_3 = QtWidgets.QLabel(self.groupBox_1) self.label_3.setObjectName("label_3") self.gridLayout_4.addWidget(self.label_3, 0, 0, 1, 1) self.checkBox_autoZShift = QtWidgets.QCheckBox(self.groupBox_1) self.checkBox_autoZShift.setObjectName("checkBox_autoZShift") self.gridLayout_4.addWidget(self.checkBox_autoZShift, 2, 2, 1, 1) self.lineEdit_zFactor = QtWidgets.QLineEdit(self.groupBox_1) self.lineEdit_zFactor.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.lineEdit_zFactor.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_zFactor.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.lineEdit_zFactor.setObjectName("lineEdit_zFactor") self.gridLayout_4.addWidget(self.lineEdit_zFactor, 1, 1, 1, 2) self.label = QtWidgets.QLabel(self.groupBox_1) self.label.setObjectName("label") self.gridLayout_4.addWidget(self.label, 1, 0, 1, 1) self.lineEdit_zShift = QtWidgets.QLineEdit(self.groupBox_1) self.lineEdit_zShift.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_zShift.setObjectName("lineEdit_zShift") self.gridLayout_4.addWidget(self.lineEdit_zShift, 2, 1, 1, 1) self.verticalLayout_3.addWidget(self.groupBox_1) self.groupBox_4 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_4.setObjectName("groupBox_4") self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_4) self.gridLayout_3.setObjectName("gridLayout_3") self.checkBox_FixAspectRatio = QtWidgets.QCheckBox(self.groupBox_4) self.checkBox_FixAspectRatio.setObjectName("checkBox_FixAspectRatio") self.gridLayout_3.addWidget(self.checkBox_FixAspectRatio, 4, 0, 1, 1) self.radioButton_FixedExtent = QtWidgets.QRadioButton(self.groupBox_4) self.radioButton_FixedExtent.setObjectName("radioButton_FixedExtent") self.gridLayout_3.addWidget(self.radioButton_FixedExtent, 1, 0, 1, 1) self.radioButton_UseCanvasExtent = QtWidgets.QRadioButton(self.groupBox_4) self.radioButton_UseCanvasExtent.setChecked(True) self.radioButton_UseCanvasExtent.setObjectName("radioButton_UseCanvasExtent") self.gridLayout_3.addWidget(self.radioButton_UseCanvasExtent, 0, 0, 1, 1) self.gridLayout_Extent = QtWidgets.QGridLayout() self.gridLayout_Extent.setObjectName("gridLayout_Extent") self.label_10 = QtWidgets.QLabel(self.groupBox_4) self.label_10.setObjectName("label_10") self.gridLayout_Extent.addWidget(self.label_10, 7, 0, 1, 1) self.label_7 = QtWidgets.QLabel(self.groupBox_4) self.label_7.setObjectName("label_7") self.gridLayout_Extent.addWidget(self.label_7, 4, 2, 1, 1) self.label_6 = QtWidgets.QLabel(self.groupBox_4) self.label_6.setObjectName("label_6") self.gridLayout_Extent.addWidget(self.label_6, 4, 0, 1, 1) self.lineEdit_CenterY = QtWidgets.QLineEdit(self.groupBox_4) self.lineEdit_CenterY.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_CenterY.setObjectName("lineEdit_CenterY") self.gridLayout_Extent.addWidget(self.lineEdit_CenterY, 1, 3, 1, 1) self.label_8 = QtWidgets.QLabel(self.groupBox_4) self.label_8.setObjectName("label_8") self.gridLayout_Extent.addWidget(self.label_8, 1, 2, 1, 1) self.label_5 = QtWidgets.QLabel(self.groupBox_4) self.label_5.setObjectName("label_5") self.gridLayout_Extent.addWidget(self.label_5, 1, 0, 1, 1) self.pushButton_SelectExtent = QtWidgets.QPushButton(self.groupBox_4) self.pushButton_SelectExtent.setObjectName("pushButton_SelectExtent") self.gridLayout_Extent.addWidget(self.pushButton_SelectExtent, 7, 3, 1, 1) self.lineEdit_Height = QtWidgets.QLineEdit(self.groupBox_4) self.lineEdit_Height.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_Height.setObjectName("lineEdit_Height") self.gridLayout_Extent.addWidget(self.lineEdit_Height, 4, 3, 1, 1) self.lineEdit_Width = QtWidgets.QLineEdit(self.groupBox_4) self.lineEdit_Width.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_Width.setObjectName("lineEdit_Width") self.gridLayout_Extent.addWidget(self.lineEdit_Width, 4, 1, 1, 1) self.lineEdit_Rotation = QtWidgets.QLineEdit(self.groupBox_4) self.lineEdit_Rotation.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly) self.lineEdit_Rotation.setObjectName("lineEdit_Rotation") self.gridLayout_Extent.addWidget(self.lineEdit_Rotation, 7, 1, 1, 1) self.lineEdit_CenterX = QtWidgets.QLineEdit(self.groupBox_4) self.lineEdit_CenterX.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhLowercaseOnly) self.lineEdit_CenterX.setObjectName("lineEdit_CenterX") self.gridLayout_Extent.addWidget(self.lineEdit_CenterX, 1, 1, 1, 1) self.gridLayout_3.addLayout(self.gridLayout_Extent, 3, 0, 1, 1) self.verticalLayout_3.addWidget(self.groupBox_4) self.groupBox = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox.setObjectName("groupBox") self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox) self.gridLayout_5.setObjectName("gridLayout_5") self.label_4 = QtWidgets.QLabel(self.groupBox) self.label_4.setObjectName("label_4") self.gridLayout_5.addWidget(self.label_4, 0, 0, 1, 1) self.checkBox_Outline = QtWidgets.QCheckBox(self.groupBox) self.checkBox_Outline.setObjectName("checkBox_Outline") self.gridLayout_5.addWidget(self.checkBox_Outline, 1, 0, 1, 2) self.comboBox_MaterialType = QtWidgets.QComboBox(self.groupBox) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.comboBox_MaterialType.sizePolicy().hasHeightForWidth()) self.comboBox_MaterialType.setSizePolicy(sizePolicy) self.comboBox_MaterialType.setObjectName("comboBox_MaterialType") self.gridLayout_5.addWidget(self.comboBox_MaterialType, 0, 1, 1, 1) self.verticalLayout_3.addWidget(self.groupBox) self.groupBox_2 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_2.setObjectName("groupBox_2") self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_2) self.gridLayout_2.setObjectName("gridLayout_2") self.radioButton_Sky = QtWidgets.QRadioButton(self.groupBox_2) self.radioButton_Sky.setChecked(True) self.radioButton_Sky.setObjectName("radioButton_Sky") self.gridLayout_2.addWidget(self.radioButton_Sky, 0, 0, 1, 2) self.radioButton_Color = QtWidgets.QRadioButton(self.groupBox_2) self.radioButton_Color.setMinimumSize(QtCore.QSize(110, 0)) self.radioButton_Color.setObjectName("radioButton_Color") self.gridLayout_2.addWidget(self.radioButton_Color, 2, 0, 1, 1) self.colorButton_Color = QgsColorButton(self.groupBox_2) self.colorButton_Color.setEnabled(False) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.colorButton_Color.sizePolicy().hasHeightForWidth()) self.colorButton_Color.setSizePolicy(sizePolicy) self.colorButton_Color.setObjectName("colorButton_Color") self.gridLayout_2.addWidget(self.colorButton_Color, 2, 1, 1, 1) self.verticalLayout_3.addWidget(self.groupBox_2) self.groupBox_3 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_3.setObjectName("groupBox_3") self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_3) self.verticalLayout.setObjectName("verticalLayout") self.radioButton_ProjectCRS = QtWidgets.QRadioButton(self.groupBox_3) self.radioButton_ProjectCRS.setChecked(True) self.radioButton_ProjectCRS.setObjectName("radioButton_ProjectCRS") self.verticalLayout.addWidget(self.radioButton_ProjectCRS) self.radioButton_WGS84 = QtWidgets.QRadioButton(self.groupBox_3) self.radioButton_WGS84.setObjectName("radioButton_WGS84") self.verticalLayout.addWidget(self.radioButton_WGS84) self.verticalLayout_3.addWidget(self.groupBox_3) spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_3.addItem(spacerItem) self.retranslateUi(ScenePropertiesWidget) self.radioButton_Color.toggled['bool'].connect(self.colorButton_Color.setEnabled) QtCore.QMetaObject.connectSlotsByName(ScenePropertiesWidget) ScenePropertiesWidget.setTabOrder(self.lineEdit_BaseSize, self.lineEdit_zFactor) ScenePropertiesWidget.setTabOrder(self.lineEdit_zFactor, self.lineEdit_zShift) ScenePropertiesWidget.setTabOrder(self.lineEdit_zShift, self.checkBox_autoZShift) ScenePropertiesWidget.setTabOrder(self.checkBox_autoZShift, self.radioButton_UseCanvasExtent) ScenePropertiesWidget.setTabOrder(self.radioButton_UseCanvasExtent, self.radioButton_FixedExtent) ScenePropertiesWidget.setTabOrder(self.radioButton_FixedExtent, self.lineEdit_CenterX) ScenePropertiesWidget.setTabOrder(self.lineEdit_CenterX, self.lineEdit_CenterY) ScenePropertiesWidget.setTabOrder(self.lineEdit_CenterY, self.lineEdit_Width) ScenePropertiesWidget.setTabOrder(self.lineEdit_Width, self.lineEdit_Height) ScenePropertiesWidget.setTabOrder(self.lineEdit_Height, self.lineEdit_Rotation) ScenePropertiesWidget.setTabOrder(self.lineEdit_Rotation, self.pushButton_SelectExtent) ScenePropertiesWidget.setTabOrder(self.pushButton_SelectExtent, self.checkBox_FixAspectRatio) ScenePropertiesWidget.setTabOrder(self.checkBox_FixAspectRatio, self.comboBox_MaterialType) ScenePropertiesWidget.setTabOrder(self.comboBox_MaterialType, self.checkBox_Outline) ScenePropertiesWidget.setTabOrder(self.checkBox_Outline, self.radioButton_Sky) ScenePropertiesWidget.setTabOrder(self.radioButton_Sky, self.radioButton_Color) ScenePropertiesWidget.setTabOrder(self.radioButton_Color, self.colorButton_Color) ScenePropertiesWidget.setTabOrder(self.colorButton_Color, self.radioButton_ProjectCRS) ScenePropertiesWidget.setTabOrder(self.radioButton_ProjectCRS, self.radioButton_WGS84) def retranslateUi(self, ScenePropertiesWidget): _translate = QtCore.QCoreApplication.translate ScenePropertiesWidget.setWindowTitle(_translate("ScenePropertiesWidget", "Form")) self.groupBox_1.setTitle(_translate("ScenePropertiesWidget", "&World Coordinates")) self.label_2.setText(_translate("ScenePropertiesWidget", "Vertical shift")) self.label_3.setText(_translate("ScenePropertiesWidget", "Base width")) self.checkBox_autoZShift.setToolTip(_translate("ScenePropertiesWidget", "Automatic vertical shift adjustment")) self.checkBox_autoZShift.setText(_translate("ScenePropertiesWidget", "Auto")) self.label.setText(_translate("ScenePropertiesWidget", "Vertical exaggeration")) self.groupBox_4.setTitle(_translate("ScenePropertiesWidget", "Map &Extent")) self.checkBox_FixAspectRatio.setText(_translate("ScenePropertiesWidget", "Fix aspect ratio to 1:1")) self.radioButton_FixedExtent.setText(_translate("ScenePropertiesWidget", "Fixed extent")) self.radioButton_UseCanvasExtent.setText(_translate("ScenePropertiesWidget", "Use map canvas extent")) self.label_10.setText(_translate("ScenePropertiesWidget", "Rotation")) self.label_7.setText(_translate("ScenePropertiesWidget", "Height")) self.label_6.setText(_translate("ScenePropertiesWidget", "Width")) self.label_8.setText(_translate("ScenePropertiesWidget", "Center Y")) self.label_5.setText(_translate("ScenePropertiesWidget", "Center X")) self.pushButton_SelectExtent.setText(_translate("ScenePropertiesWidget", "Select...")) self.groupBox.setTitle(_translate("ScenePropertiesWidget", "&Material and Effect")) self.label_4.setText(_translate("ScenePropertiesWidget", "Material type")) self.checkBox_Outline.setToolTip(_translate("ScenePropertiesWidget", "Check this to enable outline effect")) self.checkBox_Outline.setText(_translate("ScenePropertiesWidget", "Enable outline effect")) self.groupBox_2.setTitle(_translate("ScenePropertiesWidget", "&Background")) self.radioButton_Sky.setText(_translate("ScenePropertiesWidget", "Sky")) self.radioButton_Color.setText(_translate("ScenePropertiesWidget", "Solid color")) self.groupBox_3.setTitle(_translate("ScenePropertiesWidget", "&Display of Coordinates")) self.radioButton_ProjectCRS.setText(_translate("ScenePropertiesWidget", "Coordinates in the project CRS")) self.radioButton_WGS84.setText(_translate("ScenePropertiesWidget", "Latitude and longitude (WGS84)"))
class Ui_ScenePropertiesWidget(object): def setupUi(self, ScenePropertiesWidget): ScenePropertiesWidget.setObjectName("ScenePropertiesWidget") ScenePropertiesWidget.resize(328, 411) self.verticalLayout_3 = QtWidgets.QVBoxLayout(ScenePropertiesWidget) self.verticalLayout_3.setObjectName("verticalLayout_3") self.groupBox_1 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_1.setObjectName("groupBox_1") self.formLayout_3 = QtWidgets.QFormLayout(self.groupBox_1) self.formLayout_3.setObjectName("formLayout_3") self.label_3 = QtWidgets.QLabel(self.groupBox_1) self.label_3.setObjectName("label_3") self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_3) self.lineEdit_BaseSize = QtWidgets.QLineEdit(self.groupBox_1) self.lineEdit_BaseSize.setInputMethodHints(QtCore.Qt.ImhDigitsOnly) self.lineEdit_BaseSize.setObjectName("lineEdit_BaseSize") self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit_BaseSize) self.label = QtWidgets.QLabel(self.groupBox_1) self.label.setObjectName("label") self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label) self.lineEdit_zFactor = QtWidgets.QLineEdit(self.groupBox_1) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lineEdit_zFactor.sizePolicy().hasHeightForWidth()) self.lineEdit_zFactor.setSizePolicy(sizePolicy) self.lineEdit_zFactor.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.lineEdit_zFactor.setInputMethodHints(QtCore.Qt.ImhDigitsOnly) self.lineEdit_zFactor.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.lineEdit_zFactor.setObjectName("lineEdit_zFactor") self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_zFactor) self.label_2 = QtWidgets.QLabel(self.groupBox_1) self.label_2.setObjectName("label_2") self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.lineEdit_zShift = QtWidgets.QLineEdit(self.groupBox_1) self.lineEdit_zShift.setObjectName("lineEdit_zShift") self.horizontalLayout.addWidget(self.lineEdit_zShift) self.checkBox_autoZShift = QtWidgets.QCheckBox(self.groupBox_1) self.checkBox_autoZShift.setObjectName("checkBox_autoZShift") self.horizontalLayout.addWidget(self.checkBox_autoZShift) self.formLayout_3.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout) self.verticalLayout_3.addWidget(self.groupBox_1) self.groupBox = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox.setObjectName("groupBox") self.formLayout = QtWidgets.QFormLayout(self.groupBox) self.formLayout.setObjectName("formLayout") self.label_4 = QtWidgets.QLabel(self.groupBox) self.label_4.setObjectName("label_4") self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_4) self.comboBox_MaterialType = QtWidgets.QComboBox(self.groupBox) self.comboBox_MaterialType.setObjectName("comboBox_MaterialType") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox_MaterialType) self.verticalLayout_3.addWidget(self.groupBox) self.groupBox_2 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_2.setObjectName("groupBox_2") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2) self.verticalLayout_2.setObjectName("verticalLayout_2") self.radioButton_Sky = QtWidgets.QRadioButton(self.groupBox_2) self.radioButton_Sky.setChecked(True) self.radioButton_Sky.setObjectName("radioButton_Sky") self.verticalLayout_2.addWidget(self.radioButton_Sky) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.radioButton_Color = QtWidgets.QRadioButton(self.groupBox_2) self.radioButton_Color.setMinimumSize(QtCore.QSize(110, 0)) self.radioButton_Color.setObjectName("radioButton_Color") self.horizontalLayout_2.addWidget(self.radioButton_Color) self.colorButton_Color = QgsColorButton(self.groupBox_2) self.colorButton_Color.setEnabled(False) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.colorButton_Color.sizePolicy().hasHeightForWidth()) self.colorButton_Color.setSizePolicy(sizePolicy) self.colorButton_Color.setObjectName("colorButton_Color") self.horizontalLayout_2.addWidget(self.colorButton_Color) self.verticalLayout_2.addLayout(self.horizontalLayout_2) self.verticalLayout_3.addWidget(self.groupBox_2) self.groupBox_3 = QtWidgets.QGroupBox(ScenePropertiesWidget) self.groupBox_3.setObjectName("groupBox_3") self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_3) self.verticalLayout.setObjectName("verticalLayout") self.radioButton_ProjectCRS = QtWidgets.QRadioButton(self.groupBox_3) self.radioButton_ProjectCRS.setChecked(True) self.radioButton_ProjectCRS.setObjectName("radioButton_ProjectCRS") self.verticalLayout.addWidget(self.radioButton_ProjectCRS) self.radioButton_WGS84 = QtWidgets.QRadioButton(self.groupBox_3) self.radioButton_WGS84.setObjectName("radioButton_WGS84") self.verticalLayout.addWidget(self.radioButton_WGS84) self.verticalLayout_3.addWidget(self.groupBox_3) spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_3.addItem(spacerItem) self.retranslateUi(ScenePropertiesWidget) self.radioButton_Color.toggled['bool'].connect(self.colorButton_Color.setEnabled) QtCore.QMetaObject.connectSlotsByName(ScenePropertiesWidget) ScenePropertiesWidget.setTabOrder(self.lineEdit_BaseSize, self.lineEdit_zFactor) ScenePropertiesWidget.setTabOrder(self.lineEdit_zFactor, self.lineEdit_zShift) ScenePropertiesWidget.setTabOrder(self.lineEdit_zShift, self.checkBox_autoZShift) ScenePropertiesWidget.setTabOrder(self.checkBox_autoZShift, self.comboBox_MaterialType) ScenePropertiesWidget.setTabOrder(self.comboBox_MaterialType, self.radioButton_Sky) ScenePropertiesWidget.setTabOrder(self.radioButton_Sky, self.radioButton_Color) ScenePropertiesWidget.setTabOrder(self.radioButton_Color, self.colorButton_Color) ScenePropertiesWidget.setTabOrder(self.colorButton_Color, self.radioButton_ProjectCRS) ScenePropertiesWidget.setTabOrder(self.radioButton_ProjectCRS, self.radioButton_WGS84) def retranslateUi(self, ScenePropertiesWidget): _translate = QtCore.QCoreApplication.translate ScenePropertiesWidget.setWindowTitle(_translate("ScenePropertiesWidget", "Form")) self.groupBox_1.setTitle(_translate("ScenePropertiesWidget", "&World Coordinates")) self.label_3.setText(_translate("ScenePropertiesWidget", "Base width")) self.label.setText(_translate("ScenePropertiesWidget", "Vertical exaggeration")) self.label_2.setText(_translate("ScenePropertiesWidget", "Vertical shift")) self.checkBox_autoZShift.setToolTip(_translate("ScenePropertiesWidget", "Automatic vertical shift adjustment")) self.checkBox_autoZShift.setText(_translate("ScenePropertiesWidget", "Auto")) self.groupBox.setTitle(_translate("ScenePropertiesWidget", "Material")) self.label_4.setText(_translate("ScenePropertiesWidget", "Basic type")) self.groupBox_2.setTitle(_translate("ScenePropertiesWidget", "&Background")) self.radioButton_Sky.setText(_translate("ScenePropertiesWidget", "Sky")) self.radioButton_Color.setText(_translate("ScenePropertiesWidget", "Solid color")) self.groupBox_3.setTitle(_translate("ScenePropertiesWidget", "&Display of Coordinates")) self.radioButton_ProjectCRS.setText(_translate("ScenePropertiesWidget", "Coordinates in the project CRS")) self.radioButton_WGS84.setText(_translate("ScenePropertiesWidget", "Latitude and longitude (WGS84)"))
class Serval(object): def __init__(self, iface): self.iface = iface self.canvas = self.iface.mapCanvas() self.plugin_dir = os.path.dirname(__file__) self.uc = UserCommunication(iface, 'Serval') self.mode = 'probe' self.bands = None self.raster = None self.px, self.py = [0, 0] self.last_point = QgsPointXY(0, 0) self.undos = defaultdict(list) self.redos = defaultdict(list) self.qgis_project = QgsProject() self.menu = u'Serval' self.actions = [] self.toolbar = self.iface.addToolBar(u'Serval') self.toolbar.setObjectName(u'Serval') # Map tools self.probeTool = QgsMapToolEmitPoint(self.canvas) self.probeTool.setObjectName('ServalProbeTool') self.probeTool.setCursor( QCursor(QPixmap(icon_path('probe_tool.svg')), hotX=2, hotY=22)) self.probeTool.canvasClicked.connect(self.point_clicked) self.drawTool = QgsMapToolEmitPoint(self.canvas) self.drawTool.setObjectName('ServalDrawTool') self.drawTool.setCursor( QCursor(QPixmap(icon_path('draw_tool.svg')), hotX=2, hotY=22)) self.drawTool.canvasClicked.connect(self.point_clicked) self.gomTool = QgsMapToolEmitPoint(self.canvas) self.gomTool.setObjectName('ServalGomTool') self.gomTool.setCursor( QCursor(QPixmap(icon_path('gom_tool.svg')), hotX=5, hotY=19)) self.gomTool.canvasClicked.connect(self.point_clicked) self.mColorButton = QgsColorButton() icon1 = QIcon(icon_path('mIconColorBox.svg')) self.mColorButton.setIcon(icon1) self.mColorButton.setMinimumSize(QSize(40, 24)) self.mColorButton.setMaximumSize(QSize(40, 24)) self.mColorButton.colorChanged.connect(self.set_rgb_from_picker) self.b1SBox = BandSpinBox() self.b2SBox = BandSpinBox() self.b3SBox = BandSpinBox() self.sboxes = [self.b1SBox, self.b2SBox, self.b3SBox] for sb in self.sboxes: sb.user_hit_enter.connect(self.change_cell_value_key) self.iface.currentLayerChanged.connect(self.set_active_raster) self.qgis_project.layersAdded.connect(self.set_active_raster) self.canvas.mapToolSet.connect(self.check_active_tool) def initGui(self): # Menu and toolbar actions self.add_action('serval_icon.svg', text=u'Show Serval Toolbar', add_to_menu=True, add_to_toolbar=False, callback=self.show_toolbar, parent=self.iface.mainWindow()) self.probe_btn = self.add_action('probe.svg', text=u'Probing Mode', whats_this=u'Probing Mode', add_to_toolbar=True, callback=self.activate_probing, parent=self.iface.mainWindow()) self.draw_btn = self.add_action('draw.svg', text=u'Drawing Mode', whats_this=u'Drawing Mode', add_to_toolbar=True, callback=self.activate_drawing, parent=self.iface.mainWindow()) self.gom_btn = self.add_action( 'gom.svg', text=u'Set Raster Cell Value to NoData', whats_this=u'Set Raster Cell Value to NoData', add_to_toolbar=True, callback=self.activate_gom, parent=self.iface.mainWindow()) self.checkable_tool_btns = [ self.draw_btn, self.probe_btn, self.gom_btn ] self.def_nodata_btn = self.add_action( 'define_nodata.svg', text=u'Define/Change Raster NoData Value', whats_this=u'Define/Change Raster NoData Value', add_to_toolbar=True, callback=self.define_nodata, parent=self.iface.mainWindow()) self.toolbar.addWidget(self.mColorButton) self.setup_spin_boxes() self.undo_btn = self.add_action('undo.svg', text=u'Undo', whats_this=u'Undo', add_to_toolbar=True, callback=self.undo, parent=self.iface.mainWindow()) self.redo_btn = self.add_action('redo.svg', text=u'Redo', whats_this=u'Redo', add_to_toolbar=True, callback=self.redo, parent=self.iface.mainWindow()) self.show_help = self.add_action('help.svg', text=u'Help', whats_this=u'Help', add_to_toolbar=True, add_to_menu=True, callback=self.show_website, parent=self.iface.mainWindow()) self.set_active_raster() self.check_undo_redo_btns() def add_action(self, icon_name, text, callback, enabled_flag=True, add_to_menu=False, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): icon = QIcon(icon_path(icon_name)) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def unload(self): self.iface.actionPan().trigger() for action in self.actions: self.iface.removePluginMenu(u'Serval', action) self.iface.removeToolBarIcon(action) del self.toolbar def show_toolbar(self): if self.toolbar: self.toolbar.show() def check_active_tool(self, tool): try: if not tool.objectName() in [ 'ServalDrawTool', 'ServalProbeTool', 'ServalGomTool' ]: self.probe_btn.setChecked(False) self.draw_btn.setChecked(False) self.gom_btn.setChecked(False) except AttributeError: pass def set_checked_tool_btn(self, cur_tool_btn): for btn in self.checkable_tool_btns: if btn == cur_tool_btn: btn.setChecked(True) else: btn.setChecked(False) def activate_probing(self): self.mode = 'probe' self.canvas.setMapTool(self.probeTool) self.set_checked_tool_btn(self.probe_btn) def activate_drawing(self): self.mode = 'draw' self.canvas.setMapTool(self.drawTool) self.set_checked_tool_btn(self.draw_btn) def activate_gom(self): self.mode = 'gom' self.canvas.setMapTool(self.gomTool) self.set_checked_tool_btn(self.gom_btn) def setup_spin_boxes(self): for sbox in self.sboxes: sbox.setMinimumSize(QSize(60, 25)) sbox.setMaximumSize(QSize(60, 25)) sbox.setAlignment(Qt.AlignLeft) sbox.setButtonSymbols(QAbstractSpinBox.NoButtons) sbox.setKeyboardTracking(False) sbox.setShowClearButton(False) sbox.setExpressionsEnabled(False) sbox.setStyleSheet("") self.toolbar.addWidget(sbox) def point_clicked(self, point=None, button=None): # check if active layer is raster if self.raster is None: self.uc.bar_warn("Choose a raster to work with...", dur=3) return # check if coordinates trasformation is required canvas_srs = self.iface.mapCanvas().mapSettings().destinationCrs() if point is None: pos = self.last_point elif not canvas_srs == self.raster.crs(): project = QgsProject.instance() srs_transform = QgsCoordinateTransform(canvas_srs, self.raster.crs(), project) try: pos = srs_transform.transform(point) except QgsCsException as err: self.uc.bar_warn( "Point coordinates transformation failed! Check the raster projection:\n\n{}" .format(repr(err)), dur=5) return else: pos = QgsPointXY(point.x(), point.y()) # keep last clicked point self.last_point = pos # check if the point is within active raster bounds if self.rbounds[0] <= pos.x() <= self.rbounds[2]: self.px = int((pos.x() - self.rbounds[0]) / self.raster.rasterUnitsPerPixelX() ) # - self.gt[0]) / self.gt[1]) else: self.uc.bar_info("Out of x bounds", dur=2) return if self.rbounds[1] <= pos.y() <= self.rbounds[3]: self.py = int((self.rbounds[3] - pos.y()) / self.raster.rasterUnitsPerPixelY() ) # - self.gt[3]) / self.gt[5]) else: self.uc.bar_info("Out of y bounds", dur=2) return # probe current raster value, dict: band_nr -> value vals = self.rdp.identify(pos, QgsRaster.IdentifyFormatValue).results() # for rasters having more that 3 bands, ignore other than 1-3 bands_to_ignore = [i for i in vals.keys() if i > 3] for band_nr in bands_to_ignore: del vals[band_nr] # data types for each band dtypes = [] for nr in range(1, min(4, self.band_count + 1)): # bands data type dtypes.append(self.bands[nr]['qtype']) # check if nodata is defined if self.mode == 'gom' and self.bands[nr]['nodata'] is None: msg = 'NODATA value is not defined for one of the raster\'s bands.\n' msg += 'Please define it in raster properties dialog!' self.uc.show_warn(msg) return # if in probing mode, set band's spinbox value if self.mode == 'probe': val = vals[nr] if is_number( vals[nr]) else self.bands[nr]['nodata'] self.bands[nr]['sbox'].setValue(val) self.bands[nr]['sbox'].setFocus() self.bands[nr]['sbox'].selectAll() if not self.mode == 'probe': old_vals = [ v if v is not None else self.bands[k]['nodata'] for k, v in sorted(vals.items()) ] if self.mode == 'gom': temp_vals = [ self.bands[nr]['nodata'] for nr in sorted(vals.keys()) ] new_vals = [ int(v) if dtypes[i] < 6 else float(v) for i, v in enumerate(temp_vals) ] else: temp_vals = [ self.bands[nr]['sbox'].value() for nr in sorted(vals.keys()) ] new_vals = [ int(v) if dtypes[i] < 6 else float(v) for i, v in enumerate(temp_vals) ] # store all bands' changes to undo list self.undos[self.raster.id()].append( [old_vals, new_vals, self.px, self.py, pos]) # write the new cell value(s) self.change_cell_value(new_vals) if self.band_count > 2: self.mColorButton.setColor( QColor(self.bands[1]['sbox'].value(), self.bands[2]['sbox'].value(), self.bands[3]['sbox'].value())) def set_rgb_from_picker(self, c): """Set bands spinboxes values after color change in the color picker""" self.bands[1]['sbox'].setValue(c.red()) self.bands[2]['sbox'].setValue(c.green()) self.bands[3]['sbox'].setValue(c.blue()) def change_cell_value(self, vals, x=None, y=None): """Save new bands values to data provider""" if not self.rdp.isEditable(): success = self.rdp.setEditable(True) if not success: self.uc.show_warn('QGIS can\'t modify this type of raster') return if not x: x = self.px y = self.py for nr in range(1, min(4, self.band_count + 1)): rblock = QgsRasterBlock(self.bands[nr]['qtype'], 1, 1) rblock.setValue(0, 0, vals[nr - 1]) success = self.rdp.writeBlock(rblock, nr, x, y) if not success: self.uc.show_warn('QGIS can\'t modify this type of raster') return self.rdp.setEditable(False) self.raster.triggerRepaint() # prepare raster for next actions self.prepare_raster(True) self.check_undo_redo_btns() def change_cell_value_key(self): """Change cell value after user changes band's spinbox value and hits Enter key""" if self.last_point: pm = self.mode self.mode = 'draw' self.point_clicked() self.mode = pm def undo(self): if self.undos[self.raster.id()]: data = self.undos[self.raster.id()].pop() self.redos[self.raster.id()].append(data) else: return self.change_cell_value(data[0], data[2], data[3]) def redo(self): if self.redos[self.raster.id()]: data = self.redos[self.raster.id()].pop() self.undos[self.raster.id()].append(data) else: return self.change_cell_value(data[1], data[2], data[3]) def define_nodata(self): """Define and write a new NoData value to raster file""" if not self.raster: self.uc.bar_warn( 'Select a raster layer to define/change NoData value!') return # check if user defined additional NODATA value if self.rdp.userNoDataValues(1): note = '\nNote: there is a user defined NODATA value.\nCheck the raster properties (Transparency).' else: note = '' # first band data type dt = self.rdp.dataType(1) # current NODATA value if self.rdp.sourceHasNoDataValue(1): cur_nodata = self.rdp.sourceNoDataValue(1) if dt < 6: cur_nodata = '{0:d}'.format(int(float(cur_nodata))) else: cur_nodata = '' label = 'Define/change raster NODATA value.\n\n' label += 'Raster data type: {}.{}'.format(dtypes[dt]['name'], note) nd, ok = QInputDialog.getText(None, "Define NODATA Value", label, QLineEdit.Normal, str(cur_nodata)) if not ok: return if not is_number(nd): self.uc.show_warn('Wrong NODATA value!') return new_nodata = int(nd) if dt < 6 else float(nd) # set the NODATA value for each band res = [] for nr in range(1, min(4, self.band_count + 1)): res.append(self.rdp.setNoDataValue(nr, new_nodata)) self.rdp.sourceHasNoDataValue(nr) if False in res: self.uc.show_warn('Setting new NODATA value failed!') else: self.uc.bar_info('Succesful setting new NODATA values!', dur=2) self.prepare_raster() self.raster.triggerRepaint() def check_undo_redo_btns(self): """Enable/Disable undo and redo buttons based on availability of undo/redo steps""" try: if len(self.undos[self.raster.id()]) == 0: self.undo_btn.setDisabled(True) else: self.undo_btn.setEnabled(True) except: self.undo_btn.setDisabled(True) try: if len(self.redos[self.raster.id()]) == 0: self.redo_btn.setDisabled(True) else: self.redo_btn.setEnabled(True) except: self.redo_btn.setDisabled(True) def disable_toolbar_actions(self): # disable all toolbar actions but Help (for vectors and unsupported rasters) for action in self.actions: action.setDisabled(True) self.show_help.setEnabled(True) def check_layer(self, layer): """Check if we can work with the raster""" if layer == None \ or not layer.isValid() \ or not layer.type() == raster_layer_type \ or not (layer.dataProvider().capabilities() & QgsRasterDataProvider.Create) \ or layer.crs() is None: return False else: return True def set_active_raster(self): """Active layer has change - check if it is a raster layer and prepare it for the plugin""" if self.bands: self.bands = None for sbox in self.sboxes: sbox.setValue(0) layer = self.iface.activeLayer() if self.check_layer(layer): self.raster = layer self.rdp = layer.dataProvider() self.band_count = layer.bandCount() # is data type supported? supported = True for nr in range(1, min(4, self.band_count + 1)): if self.rdp.dataType(nr) == 0 or self.rdp.dataType(nr) > 7: t = dtypes[self.rdp.dataType(nr)]['name'] supported = False if supported: # enable all toolbar actions for action in self.actions: action.setEnabled(True) # if raster properties change, get them (refeshes view) self.raster.rendererChanged.connect(self.prepare_raster) self.prepare_raster(supported) # not supported data type else: msg = 'The raster data type is: {}.'.format(t) msg += '\nServal can\'t work with it, sorry!' self.uc.show_warn(msg) self.reset_raster() # it is not a supported raster layer else: self.reset_raster() self.check_undo_redo_btns() def reset_raster(self): self.raster = None self.mColorButton.setDisabled(True) self.prepare_raster(False) def prepare_raster(self, supported=True): """Open raster using GDAL if it is supported""" # reset bands' spin boxes for i, sbox in enumerate(self.sboxes): sbox.setProperty('bandNr', i + 1) sbox.setDisabled(True) if not supported: return if self.band_count > 2: self.mColorButton.setEnabled(True) else: self.mColorButton.setDisabled(True) extent = self.raster.extent() self.rbounds = extent.toRectF().getCoords() self.bands = {} for nr in range(1, min(4, self.band_count + 1)): self.bands[nr] = {} self.bands[nr]['sbox'] = self.sboxes[nr - 1] # NODATA if self.rdp.sourceHasNoDataValue(nr): # source nodata value? self.bands[nr]['nodata'] = self.rdp.sourceNoDataValue(nr) # use the src nodata self.rdp.setUseSourceNoDataValue(nr, True) # no nodata defined in the raster source else: # check if user defined any nodata values if self.rdp.userNoDataValues(nr): # get min nodata value from the first user nodata range nd_ranges = self.rdp.userNoDataValues(nr) self.bands[nr]['nodata'] = nd_ranges[0].min() else: # leave nodata undefined self.bands[nr]['nodata'] = None # enable band's spin box self.bands[nr]['sbox'].setEnabled(True) # get bands data type dt = self.bands[nr]['qtype'] = self.rdp.dataType(nr) # set spin boxes properties self.bands[nr]['sbox'].setMinimum(dtypes[dt]['min']) self.bands[nr]['sbox'].setMaximum(dtypes[dt]['max']) self.bands[nr]['sbox'].setDecimals(dtypes[dt]['dig']) @staticmethod def show_website(): QDesktopServices.openUrl(QUrl('https://github.com/erpas/serval/wiki'))
class Ui_DEMPropertiesWidget(object): def setupUi(self, DEMPropertiesWidget): DEMPropertiesWidget.setObjectName("DEMPropertiesWidget") DEMPropertiesWidget.resize(335, 533) self.verticalLayout_2 = QtWidgets.QVBoxLayout(DEMPropertiesWidget) self.verticalLayout_2.setObjectName("verticalLayout_2") self.groupBox_Geometry = QtWidgets.QGroupBox(DEMPropertiesWidget) self.groupBox_Geometry.setObjectName("groupBox_Geometry") self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_Geometry) self.verticalLayout_6.setContentsMargins(-1, 6, -1, 6) self.verticalLayout_6.setObjectName("verticalLayout_6") self.horizontalLayout_Resampling = QtWidgets.QHBoxLayout() self.horizontalLayout_Resampling.setObjectName( "horizontalLayout_Resampling") self.label_Resampling = QtWidgets.QLabel(self.groupBox_Geometry) self.label_Resampling.setObjectName("label_Resampling") self.horizontalLayout_Resampling.addWidget(self.label_Resampling) self.horizontalSlider_DEMSize = QtWidgets.QSlider( self.groupBox_Geometry) self.horizontalSlider_DEMSize.setEnabled(True) self.horizontalSlider_DEMSize.setMinimum(1) self.horizontalSlider_DEMSize.setMaximum(6) self.horizontalSlider_DEMSize.setSingleStep(1) self.horizontalSlider_DEMSize.setPageStep(1) self.horizontalSlider_DEMSize.setProperty("value", 2) self.horizontalSlider_DEMSize.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider_DEMSize.setTickPosition( QtWidgets.QSlider.TicksBelow) self.horizontalSlider_DEMSize.setTickInterval(1) self.horizontalSlider_DEMSize.setObjectName("horizontalSlider_DEMSize") self.horizontalLayout_Resampling.addWidget( self.horizontalSlider_DEMSize) self.label_ResamplingLevel = QtWidgets.QLabel(self.groupBox_Geometry) self.label_ResamplingLevel.setMinimumSize(QtCore.QSize(10, 0)) self.label_ResamplingLevel.setObjectName("label_ResamplingLevel") self.horizontalLayout_Resampling.addWidget(self.label_ResamplingLevel) self.verticalLayout_6.addLayout(self.horizontalLayout_Resampling) self.verticalLayout_Surroundings = QtWidgets.QVBoxLayout() self.verticalLayout_Surroundings.setObjectName( "verticalLayout_Surroundings") self.checkBox_Surroundings = QtWidgets.QCheckBox( self.groupBox_Geometry) self.checkBox_Surroundings.setObjectName("checkBox_Surroundings") self.verticalLayout_Surroundings.addWidget(self.checkBox_Surroundings) self.gridLayout_Surroundings = QtWidgets.QGridLayout() self.gridLayout_Surroundings.setObjectName("gridLayout_Surroundings") self.label_3 = QtWidgets.QLabel(self.groupBox_Geometry) self.label_3.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.label_3.setObjectName("label_3") self.gridLayout_Surroundings.addWidget(self.label_3, 0, 2, 1, 1) self.spinBox_Roughening = QtWidgets.QSpinBox(self.groupBox_Geometry) self.spinBox_Roughening.setMinimumSize(QtCore.QSize(70, 0)) self.spinBox_Roughening.setMaximum(64) self.spinBox_Roughening.setProperty("value", 1) self.spinBox_Roughening.setObjectName("spinBox_Roughening") self.gridLayout_Surroundings.addWidget(self.spinBox_Roughening, 0, 3, 1, 1) self.spinBox_Size = QtWidgets.QSpinBox(self.groupBox_Geometry) self.spinBox_Size.setMinimumSize(QtCore.QSize(70, 0)) self.spinBox_Size.setMinimum(3) self.spinBox_Size.setMaximum(9) self.spinBox_Size.setSingleStep(2) self.spinBox_Size.setProperty("value", 3) self.spinBox_Size.setObjectName("spinBox_Size") self.gridLayout_Surroundings.addWidget(self.spinBox_Size, 0, 1, 1, 1) self.label_2 = QtWidgets.QLabel(self.groupBox_Geometry) self.label_2.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.label_2.setObjectName("label_2") self.gridLayout_Surroundings.addWidget(self.label_2, 0, 0, 1, 1) self.verticalLayout_Surroundings.addLayout( self.gridLayout_Surroundings) self.verticalLayout_6.addLayout(self.verticalLayout_Surroundings) self.verticalLayout_Clip = QtWidgets.QVBoxLayout() self.verticalLayout_Clip.setObjectName("verticalLayout_Clip") self.checkBox_Clip = QtWidgets.QCheckBox(self.groupBox_Geometry) self.checkBox_Clip.setObjectName("checkBox_Clip") self.verticalLayout_Clip.addWidget(self.checkBox_Clip) self.comboBox_ClipLayer = QtWidgets.QComboBox(self.groupBox_Geometry) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.comboBox_ClipLayer.sizePolicy().hasHeightForWidth()) self.comboBox_ClipLayer.setSizePolicy(sizePolicy) self.comboBox_ClipLayer.setMaximumSize(QtCore.QSize(220, 16777215)) self.comboBox_ClipLayer.setObjectName("comboBox_ClipLayer") self.verticalLayout_Clip.addWidget(self.comboBox_ClipLayer) self.verticalLayout_6.addLayout(self.verticalLayout_Clip) self.verticalLayout_2.addWidget(self.groupBox_Geometry) self.groupBox_Material = QtWidgets.QGroupBox(DEMPropertiesWidget) self.groupBox_Material.setObjectName("groupBox_Material") self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_Material) self.verticalLayout_4.setContentsMargins(-1, 6, -1, 6) self.verticalLayout_4.setObjectName("verticalLayout_4") self.label_5 = QtWidgets.QLabel(self.groupBox_Material) self.label_5.setObjectName("label_5") self.verticalLayout_4.addWidget(self.label_5) self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setSpacing(4) self.verticalLayout.setObjectName("verticalLayout") self.radioButton_MapCanvas = QtWidgets.QRadioButton( self.groupBox_Material) self.radioButton_MapCanvas.setChecked(True) self.radioButton_MapCanvas.setObjectName("radioButton_MapCanvas") self.verticalLayout.addWidget(self.radioButton_MapCanvas) self.horizontalLayout_5 = QtWidgets.QHBoxLayout() self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.radioButton_LayerImage = QtWidgets.QRadioButton( self.groupBox_Material) self.radioButton_LayerImage.setObjectName("radioButton_LayerImage") self.horizontalLayout_5.addWidget(self.radioButton_LayerImage) self.label_LayerImage = QtWidgets.QLabel(self.groupBox_Material) self.label_LayerImage.setEnabled(False) self.label_LayerImage.setText("") self.label_LayerImage.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.label_LayerImage.setObjectName("label_LayerImage") self.horizontalLayout_5.addWidget(self.label_LayerImage) self.toolButton_SelectLayer = QtWidgets.QToolButton( self.groupBox_Material) self.toolButton_SelectLayer.setEnabled(False) self.toolButton_SelectLayer.setObjectName("toolButton_SelectLayer") self.horizontalLayout_5.addWidget(self.toolButton_SelectLayer) self.verticalLayout.addLayout(self.horizontalLayout_5) self.horizontalLayout_ImageFile = QtWidgets.QHBoxLayout() self.horizontalLayout_ImageFile.setObjectName( "horizontalLayout_ImageFile") self.radioButton_ImageFile = QtWidgets.QRadioButton( self.groupBox_Material) self.radioButton_ImageFile.setEnabled(True) self.radioButton_ImageFile.setObjectName("radioButton_ImageFile") self.horizontalLayout_ImageFile.addWidget(self.radioButton_ImageFile) self.lineEdit_ImageFile = QtWidgets.QLineEdit(self.groupBox_Material) self.lineEdit_ImageFile.setEnabled(False) self.lineEdit_ImageFile.setObjectName("lineEdit_ImageFile") self.horizontalLayout_ImageFile.addWidget(self.lineEdit_ImageFile) self.toolButton_ImageFile = QtWidgets.QToolButton( self.groupBox_Material) self.toolButton_ImageFile.setEnabled(False) self.toolButton_ImageFile.setObjectName("toolButton_ImageFile") self.horizontalLayout_ImageFile.addWidget(self.toolButton_ImageFile) self.verticalLayout.addLayout(self.horizontalLayout_ImageFile) self.horizontalLayout_7 = QtWidgets.QHBoxLayout() self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.radioButton_SolidColor = QtWidgets.QRadioButton( self.groupBox_Material) self.radioButton_SolidColor.setObjectName("radioButton_SolidColor") self.horizontalLayout_7.addWidget(self.radioButton_SolidColor) self.colorButton_Color = QgsColorButton(self.groupBox_Material) self.colorButton_Color.setEnabled(False) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.colorButton_Color.sizePolicy().hasHeightForWidth()) self.colorButton_Color.setSizePolicy(sizePolicy) self.colorButton_Color.setObjectName("colorButton_Color") self.horizontalLayout_7.addWidget(self.colorButton_Color) self.verticalLayout.addLayout(self.horizontalLayout_7) self.verticalLayout_4.addLayout(self.verticalLayout) self.gridLayout = QtWidgets.QGridLayout() self.gridLayout.setObjectName("gridLayout") self.horizontalSlider_Opacity = QtWidgets.QSlider( self.groupBox_Material) self.horizontalSlider_Opacity.setMaximum(100) self.horizontalSlider_Opacity.setProperty("value", 100) self.horizontalSlider_Opacity.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider_Opacity.setObjectName("horizontalSlider_Opacity") self.gridLayout.addWidget(self.horizontalSlider_Opacity, 2, 2, 1, 1) self.label_17 = QtWidgets.QLabel(self.groupBox_Material) self.label_17.setObjectName("label_17") self.gridLayout.addWidget(self.label_17, 2, 0, 1, 1) self.label_TextureSize = QtWidgets.QLabel(self.groupBox_Material) self.label_TextureSize.setObjectName("label_TextureSize") self.gridLayout.addWidget(self.label_TextureSize, 0, 0, 1, 1) self.spinBox_Opacity = QtWidgets.QSpinBox(self.groupBox_Material) self.spinBox_Opacity.setEnabled(True) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.spinBox_Opacity.sizePolicy().hasHeightForWidth()) self.spinBox_Opacity.setSizePolicy(sizePolicy) self.spinBox_Opacity.setPrefix("") self.spinBox_Opacity.setMinimum(0) self.spinBox_Opacity.setMaximum(100) self.spinBox_Opacity.setSingleStep(1) self.spinBox_Opacity.setProperty("value", 100) self.spinBox_Opacity.setObjectName("spinBox_Opacity") self.gridLayout.addWidget(self.spinBox_Opacity, 2, 3, 1, 1) self.comboBox_TextureSize = QtWidgets.QComboBox(self.groupBox_Material) self.comboBox_TextureSize.setObjectName("comboBox_TextureSize") self.gridLayout.addWidget(self.comboBox_TextureSize, 0, 1, 1, 3) self.verticalLayout_4.addLayout(self.gridLayout) self.checkBox_TransparentBackground = QtWidgets.QCheckBox( self.groupBox_Material) self.checkBox_TransparentBackground.setObjectName( "checkBox_TransparentBackground") self.verticalLayout_4.addWidget(self.checkBox_TransparentBackground) self.checkBox_Shading = QtWidgets.QCheckBox(self.groupBox_Material) self.checkBox_Shading.setChecked(True) self.checkBox_Shading.setObjectName("checkBox_Shading") self.verticalLayout_4.addWidget(self.checkBox_Shading) self.verticalLayout_2.addWidget(self.groupBox_Material) self.groupBox_Others = QtWidgets.QGroupBox(DEMPropertiesWidget) self.groupBox_Others.setObjectName("groupBox_Others") self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_Others) self.verticalLayout_5.setContentsMargins(-1, 6, -1, 6) self.verticalLayout_5.setObjectName("verticalLayout_5") self.checkBox_Sides = QtWidgets.QCheckBox(self.groupBox_Others) self.checkBox_Sides.setChecked(True) self.checkBox_Sides.setObjectName("checkBox_Sides") self.verticalLayout_5.addWidget(self.checkBox_Sides) self.checkBox_Frame = QtWidgets.QCheckBox(self.groupBox_Others) self.checkBox_Frame.setObjectName("checkBox_Frame") self.verticalLayout_5.addWidget(self.checkBox_Frame) self.checkBox_Visible = QtWidgets.QCheckBox(self.groupBox_Others) self.checkBox_Visible.setChecked(True) self.checkBox_Visible.setObjectName("checkBox_Visible") self.verticalLayout_5.addWidget(self.checkBox_Visible) self.verticalLayout_2.addWidget(self.groupBox_Others) spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem) self.retranslateUi(DEMPropertiesWidget) self.checkBox_Clip.toggled['bool'].connect( self.comboBox_ClipLayer.setVisible) self.radioButton_LayerImage.toggled['bool'].connect( self.label_LayerImage.setEnabled) self.radioButton_LayerImage.toggled['bool'].connect( self.toolButton_SelectLayer.setEnabled) self.radioButton_ImageFile.toggled['bool'].connect( self.lineEdit_ImageFile.setEnabled) self.radioButton_ImageFile.toggled['bool'].connect( self.toolButton_ImageFile.setEnabled) self.checkBox_Clip.toggled['bool'].connect( self.checkBox_Frame.setDisabled) self.horizontalSlider_DEMSize.valueChanged['int'].connect( self.label_ResamplingLevel.setNum) self.horizontalSlider_Opacity.valueChanged['int'].connect( self.spinBox_Opacity.setValue) self.spinBox_Opacity.valueChanged['int'].connect( self.horizontalSlider_Opacity.setValue) self.radioButton_SolidColor.toggled['bool'].connect( self.colorButton_Color.setEnabled) QtCore.QMetaObject.connectSlotsByName(DEMPropertiesWidget) DEMPropertiesWidget.setTabOrder(self.horizontalSlider_DEMSize, self.checkBox_Surroundings) DEMPropertiesWidget.setTabOrder(self.checkBox_Surroundings, self.spinBox_Size) DEMPropertiesWidget.setTabOrder(self.spinBox_Size, self.spinBox_Roughening) DEMPropertiesWidget.setTabOrder(self.spinBox_Roughening, self.checkBox_Clip) DEMPropertiesWidget.setTabOrder(self.checkBox_Clip, self.comboBox_ClipLayer) DEMPropertiesWidget.setTabOrder(self.comboBox_ClipLayer, self.radioButton_MapCanvas) DEMPropertiesWidget.setTabOrder(self.radioButton_MapCanvas, self.radioButton_LayerImage) DEMPropertiesWidget.setTabOrder(self.radioButton_LayerImage, self.toolButton_SelectLayer) DEMPropertiesWidget.setTabOrder(self.toolButton_SelectLayer, self.radioButton_ImageFile) DEMPropertiesWidget.setTabOrder(self.radioButton_ImageFile, self.lineEdit_ImageFile) DEMPropertiesWidget.setTabOrder(self.lineEdit_ImageFile, self.toolButton_ImageFile) DEMPropertiesWidget.setTabOrder(self.toolButton_ImageFile, self.radioButton_SolidColor) DEMPropertiesWidget.setTabOrder(self.radioButton_SolidColor, self.comboBox_TextureSize) DEMPropertiesWidget.setTabOrder(self.comboBox_TextureSize, self.horizontalSlider_Opacity) DEMPropertiesWidget.setTabOrder(self.horizontalSlider_Opacity, self.spinBox_Opacity) DEMPropertiesWidget.setTabOrder(self.spinBox_Opacity, self.checkBox_TransparentBackground) DEMPropertiesWidget.setTabOrder(self.checkBox_TransparentBackground, self.checkBox_Shading) DEMPropertiesWidget.setTabOrder(self.checkBox_Shading, self.checkBox_Sides) DEMPropertiesWidget.setTabOrder(self.checkBox_Sides, self.checkBox_Frame) DEMPropertiesWidget.setTabOrder(self.checkBox_Frame, self.checkBox_Visible) def retranslateUi(self, DEMPropertiesWidget): _translate = QtCore.QCoreApplication.translate DEMPropertiesWidget.setWindowTitle( _translate("DEMPropertiesWidget", "Form")) self.groupBox_Geometry.setTitle( _translate("DEMPropertiesWidget", "&Geometry")) self.label_Resampling.setText( _translate("DEMPropertiesWidget", "Resampling level")) self.label_ResamplingLevel.setText( _translate("DEMPropertiesWidget", "2")) self.checkBox_Surroundings.setText( _translate("DEMPropertiesWidget", "Surroundings")) self.label_3.setText(_translate("DEMPropertiesWidget", "Roughening")) self.label_2.setText(_translate("DEMPropertiesWidget", "Size")) self.checkBox_Clip.setText( _translate("DEMPropertiesWidget", "Clip DEM with polygon layer")) self.groupBox_Material.setTitle( _translate("DEMPropertiesWidget", "&Material")) self.label_5.setText(_translate("DEMPropertiesWidget", "Display type")) self.radioButton_MapCanvas.setText( _translate("DEMPropertiesWidget", "Map canvas image")) self.radioButton_LayerImage.setText( _translate("DEMPropertiesWidget", "Layer image")) self.toolButton_SelectLayer.setText( _translate("DEMPropertiesWidget", "Select layer(s)...")) self.radioButton_ImageFile.setText( _translate("DEMPropertiesWidget", "Image file")) self.toolButton_ImageFile.setText( _translate("DEMPropertiesWidget", "Browse...")) self.radioButton_SolidColor.setText( _translate("DEMPropertiesWidget", "Solid color")) self.label_17.setText(_translate("DEMPropertiesWidget", "Opacity (%)")) self.label_TextureSize.setText( _translate("DEMPropertiesWidget", "Resolution")) self.checkBox_TransparentBackground.setText( _translate("DEMPropertiesWidget", "Transparent background")) self.checkBox_Shading.setText( _translate("DEMPropertiesWidget", "Enable shading")) self.groupBox_Others.setTitle( _translate("DEMPropertiesWidget", "&Other Options")) self.checkBox_Sides.setText( _translate("DEMPropertiesWidget", "Build sides")) self.checkBox_Frame.setText( _translate("DEMPropertiesWidget", "Build frame")) self.checkBox_Visible.setText( _translate("DEMPropertiesWidget", "Visible on load"))
class Serval(object): LINE_SELECTION = "line" POLYGON_SELECTION = "polygon" RGB = "RGB" SINGLE_BAND = "Single band" def __init__(self, iface): self.iface = iface self.canvas = self.iface.mapCanvas() self.plugin_dir = os.path.dirname(__file__) self.uc = UserCommunication(iface, 'Serval') self.load_settings() self.raster = None self.handler = None self.spin_boxes = None self.exp_dlg = None self.exp_builder = None self.block_pts_layer = None self.px, self.py = [0, 0] self.last_point = QgsPointXY(0, 0) self.rbounds = None self.changes = dict() # dict with rasters changes {raster_id: RasterChanges instance} self.project = QgsProject.instance() self.crs_transform = None self.all_touched = None self.selection_mode = None self.spatial_index_time = dict() # {layer_id: creation time} self.spatial_index = dict() # {layer_id: spatial index} self.selection_layers_count = 1 self.debug = DEBUG self.logger = get_logger() if self.debug else None self.menu = u'Serval' self.actions = [] self.actions_always_on = [] self.toolbar = self.iface.addToolBar(u'Serval Main Toolbar') self.toolbar.setObjectName(u'Serval Main Toolbar') self.toolbar.setToolTip(u'Serval Main Toolbar') self.sel_toolbar = self.iface.addToolBar(u'Serval Selection Toolbar') self.sel_toolbar.setObjectName(u'Serval Selection Toolbar') self.sel_toolbar.setToolTip(u'Serval Selection Toolbar') # Map tools self.probe_tool = QgsMapToolEmitPoint(self.canvas) self.probe_tool.setObjectName('ServalProbeTool') self.probe_tool.setCursor(QCursor(QPixmap(icon_path('probe_tool.svg')), hotX=2, hotY=22)) self.probe_tool.canvasClicked.connect(self.point_clicked) self.draw_tool = QgsMapToolEmitPoint(self.canvas) self.draw_tool.setObjectName('ServalDrawTool') self.draw_tool.setCursor(QCursor(QPixmap(icon_path('draw_tool.svg')), hotX=2, hotY=22)) self.draw_tool.canvasClicked.connect(self.point_clicked) self.selection_tool = RasterCellSelectionMapTool(self.iface, self.uc, self.raster, debug=self.debug) self.selection_tool.setObjectName('RasterSelectionTool') self.map_tool_btn = dict() # {map tool: button activating the tool} self.iface.currentLayerChanged.connect(self.set_active_raster) self.project.layersAdded.connect(self.set_active_raster) self.canvas.mapToolSet.connect(self.check_active_tool) self.register_exp_functions() def load_settings(self): """Return plugin settings dict - default values are overriden by user prefered values from QSettings.""" self.default_settings = { "undo_steps": {"value": 3, "vtype": int}, } self.settings = dict() s = QSettings() s.beginGroup("serval") for k, v in self.default_settings.items(): user_val = s.value(k, v["value"], v["vtype"]) self.settings[k] = user_val def edit_settings(self): """Open dialog with plugin settings.""" s = QSettings() s.beginGroup("serval") k = "undo_steps" cur_val = self.settings[k] val_type = self.default_settings[k]["vtype"] cur_steps = s.value(k, cur_val, val_type) label = 'Nr of Undo/Redo steps:' steps, ok = QInputDialog.getInt(None, "Serval Settings", label, cur_steps) if not ok: return if steps >= 0: s.setValue("undo_steps", steps) self.load_settings() self.uc.show_info("Some new settings may require QGIS restart.") def initGui(self): _ = self.add_action( 'serval_icon.svg', text=u'Show Serval Toolbars', add_to_menu=True, callback=self.show_toolbar, always_on=True, ) _ = self.add_action( 'serval_icon.svg', text=u'Hide Serval Toolbars', add_to_menu=True, callback=self.hide_toolbar, always_on=True, ) self.probe_btn = self.add_action( 'probe.svg', text="Probe raster", callback=self.activate_probing, add_to_toolbar=self.toolbar, checkable=True, ) self.map_tool_btn[self.probe_tool] = self.probe_btn self.color_btn = QgsColorButton() self.color_btn.setColor(Qt.gray) self.color_btn.setMinimumSize(QSize(40, 24)) self.color_btn.setMaximumSize(QSize(40, 24)) self.toolbar.addWidget(self.color_btn) self.color_picker_connection(connect=True) self.color_btn.setDisabled(True) self.toolbar.addWidget(QLabel("Band:")) self.bands_cbo = QComboBox() self.bands_cbo.addItem("1", 1) self.toolbar.addWidget(self.bands_cbo) self.bands_cbo.currentIndexChanged.connect(self.update_active_bands) self.bands_cbo.setDisabled(True) self.spin_boxes = BandBoxes() self.toolbar.addWidget(self.spin_boxes) self.spin_boxes.enter_hit.connect(self.apply_spin_box_values) self.draw_btn = self.add_action( 'draw.svg', text="Apply Value(s) To Single Cell", callback=self.activate_drawing, add_to_toolbar=self.toolbar, checkable=True, ) self.map_tool_btn[self.draw_tool] = self.draw_btn self.apply_spin_box_values_btn = self.add_action( 'apply_const_value.svg', text="Apply Value(s) to Selection", callback=self.apply_spin_box_values, add_to_toolbar=self.toolbar, ) self.gom_btn = self.add_action( 'apply_nodata_value.svg', text="Apply NoData to Selection", callback=self.apply_nodata_value, add_to_toolbar=self.toolbar, ) self.exp_dlg_btn = self.add_action( 'apply_expression_value.svg', text="Apply Expression Value To Selection", callback=self.define_expression, add_to_toolbar=self.toolbar, checkable=False, ) self.low_pass_filter_btn = self.add_action( 'apply_low_pass_filter.svg', text="Apply Low-Pass 3x3 Filter To Selection", callback=self.apply_low_pass_filter, add_to_toolbar=self.toolbar, checkable=False, ) self.undo_btn = self.add_action( 'undo.svg', text="Undo", callback=self.undo, add_to_toolbar=self.toolbar, ) self.redo_btn = self.add_action( 'redo.svg', text="Redo", callback=self.redo, add_to_toolbar=self.toolbar, ) self.set_nodata_btn = self.add_action( 'set_nodata.svg', text="Edit Raster NoData Values", callback=self.set_nodata, add_to_toolbar=self.toolbar, ) self.settings_btn = self.add_action( 'edit_settings.svg', text="Serval Settings", callback=self.edit_settings, add_to_toolbar=self.toolbar, always_on=True, ) self.show_help = self.add_action( 'help.svg', text="Help", add_to_menu=True, callback=self.show_website, add_to_toolbar=self.toolbar, always_on=True, ) # Selection Toolbar line_width_icon = QIcon(icon_path("line_width.svg")) line_width_lab = QLabel() line_width_lab.setPixmap(line_width_icon.pixmap(22, 12)) self.sel_toolbar.addWidget(line_width_lab) self.line_width_sbox = QgsDoubleSpinBox() self.line_width_sbox.setMinimumSize(QSize(50, 24)) self.line_width_sbox.setMaximumSize(QSize(50, 24)) # self.line_width_sbox.setButtonSymbols(QAbstractSpinBox.NoButtons) self.line_width_sbox.setValue(1) self.line_width_sbox.setMinimum(0.01) self.line_width_sbox.setShowClearButton(False) self.line_width_sbox.setToolTip("Selection Line Width") self.line_width_sbox.valueChanged.connect(self.update_selection_tool) self.width_unit_cbo = QComboBox() self.width_units = ("map units", "pixel width", "pixel height", "hairline",) for u in self.width_units: self.width_unit_cbo.addItem(u) self.width_unit_cbo.setToolTip("Selection Line Width Unit") self.sel_toolbar.addWidget(self.line_width_sbox) self.sel_toolbar.addWidget(self.width_unit_cbo) self.width_unit_cbo.currentIndexChanged.connect(self.update_selection_tool) self.line_select_btn = self.add_action( 'select_line.svg', text="Select Raster Cells by Line", callback=self.activate_line_selection, add_to_toolbar=self.sel_toolbar, checkable=True, ) self.polygon_select_btn = self.add_action( 'select_polygon.svg', text="Select Raster Cells by Polygon", callback=self.activate_polygon_selection, add_to_toolbar=self.sel_toolbar, checkable=True, ) self.selection_from_layer_btn = self.add_action( 'select_from_layer.svg', text="Create Selection From Layer", callback=self.selection_from_layer, add_to_toolbar=self.sel_toolbar, ) self.selection_to_layer_btn = self.add_action( 'selection_to_layer.svg', text="Create Memory Layer From Selection", callback=self.selection_to_layer, add_to_toolbar=self.sel_toolbar, ) self.clear_selection_btn = self.add_action( 'clear_selection.svg', text="Clear selection", callback=self.clear_selection, add_to_toolbar=self.sel_toolbar, ) self.toggle_all_touched_btn = self.add_action( 'all_touched.svg', text="Toggle All Touched Get Selected", callback=self.toggle_all_touched, checkable=True, checked=True, add_to_toolbar=self.sel_toolbar, ) self.all_touched = True self.enable_toolbar_actions(enable=False) self.check_undo_redo_btns() def add_action(self, icon_name, callback=None, text="", enabled_flag=True, add_to_menu=False, add_to_toolbar=None, status_tip=None, whats_this=None, checkable=False, checked=False, always_on=False): icon = QIcon(icon_path(icon_name)) action = QAction(icon, text, self.iface.mainWindow()) action.triggered.connect(callback) action.setEnabled(enabled_flag) action.setCheckable(checkable) action.setChecked(checked) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar is not None: add_to_toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) if always_on: self.actions_always_on.append(action) return action def unload(self): self.changes = None if self.selection_tool: self.selection_tool.reset() if self.spin_boxes is not None: self.spin_boxes.remove_spinboxes() for action in self.actions: self.iface.removePluginMenu('Serval', action) self.iface.removeToolBarIcon(action) del self.toolbar del self.sel_toolbar self.iface.actionPan().trigger() self.unregister_exp_functions() def show_toolbar(self): if self.toolbar: self.toolbar.show() self.sel_toolbar.show() def hide_toolbar(self): if self.toolbar: self.toolbar.hide() self.sel_toolbar.hide() @staticmethod def register_exp_functions(): QgsExpression.registerFunction(nearest_feature_attr_value) QgsExpression.registerFunction(nearest_pt_on_line_interpolate_z) QgsExpression.registerFunction(intersecting_features_attr_average) QgsExpression.registerFunction(interpolate_from_mesh) @staticmethod def unregister_exp_functions(): QgsExpression.unregisterFunction('nearest_feature_attr_value') QgsExpression.unregisterFunction('nearest_pt_on_line_interpolate_z') QgsExpression.unregisterFunction('intersecting_features_attr_average') QgsExpression.unregisterFunction('interpolate_from_mesh') def uncheck_all_btns(self): self.probe_btn.setChecked(False) self.draw_btn.setChecked(False) self.gom_btn.setChecked(False) self.line_select_btn.setChecked(False) self.polygon_select_btn.setChecked(False) def check_active_tool(self, cur_tool): self.uncheck_all_btns() if cur_tool in self.map_tool_btn: self.map_tool_btn[cur_tool].setChecked(True) if cur_tool == self.selection_tool: if self.selection_mode == self.LINE_SELECTION: self.line_select_btn.setChecked(True) else: self.polygon_select_btn.setChecked(True) def activate_probing(self): self.mode = 'probe' self.canvas.setMapTool(self.probe_tool) def define_expression(self): if not self.selection_tool.selected_geometries: self.uc.bar_warn("No selection for raster layer. Select some cells and retry...") return self.handler.select(self.selection_tool.selected_geometries, all_touched_cells=self.all_touched) self.handler.create_cell_pts_layer() if self.handler.cell_pts_layer.featureCount() == 0: self.uc.bar_warn("No selection for raster layer. Select some cells and retry...") return self.exp_dlg = QgsExpressionBuilderDialog(self.handler.cell_pts_layer) self.exp_builder = self.exp_dlg.expressionBuilder() self.exp_dlg.accepted.connect(self.apply_exp_value) self.exp_dlg.show() def apply_exp_value(self): if not self.exp_dlg.expressionText() or not self.exp_builder.isExpressionValid(): return QApplication.setOverrideCursor(Qt.WaitCursor) exp = self.exp_dlg.expressionText() idx = self.handler.cell_pts_layer.addExpressionField(exp, QgsField('exp_val', QVariant.Double)) self.handler.exp_field_idx = idx self.handler.write_block() QApplication.restoreOverrideCursor() self.raster.triggerRepaint() def activate_drawing(self): self.mode = 'draw' self.canvas.setMapTool(self.draw_tool) def get_cur_line_width(self): width_coef = { "map units": 1., "pixel width": self.raster.rasterUnitsPerPixelX(), "pixel height": self.raster.rasterUnitsPerPixelY(), "hairline": 0.000001, } return self.line_width_sbox.value() * width_coef[self.width_unit_cbo.currentText()] def set_selection_tool(self, mode): if self.raster is None: self.uc.bar_warn("Select a raster layer") return self.selection_mode = mode self.selection_tool.init_tool(self.raster, mode=self.selection_mode, line_width=self.get_cur_line_width()) self.selection_tool.set_prev_tool(self.canvas.mapTool()) self.canvas.setMapTool(self.selection_tool) def activate_line_selection(self): self.set_selection_tool(self.LINE_SELECTION) def activate_polygon_selection(self): self.set_selection_tool(self.POLYGON_SELECTION) def update_selection_tool(self): """Reactivate the selection tool with updated line width and units.""" if self.selection_mode == self.LINE_SELECTION: self.activate_line_selection() elif self.selection_mode == self.POLYGON_SELECTION: self.activate_polygon_selection() else: pass def apply_values(self, new_values): QApplication.setOverrideCursor(Qt.WaitCursor) self.handler.select(self.selection_tool.selected_geometries, all_touched_cells=self.all_touched) self.handler.write_block(new_values) QApplication.restoreOverrideCursor() self.raster.triggerRepaint() def apply_values_single_cell(self, new_vals): """Create single cell selection and apply the new values.""" cp = self.last_point if self.logger: self.logger.debug(f"Changing single cell for pt {cp}") col, row = self.handler.point_to_index([cp.x(), cp.y()]) px, py = self.handler.index_to_point(row, col, upper_left=False) d = 0.001 bbox = QgsRectangle(px - d, py - d, px + d, py + d) if self.logger: self.logger.debug(f"Changing single cell in {bbox}") QApplication.setOverrideCursor(Qt.WaitCursor) self.handler.select([QgsGeometry.fromRect(bbox)], all_touched_cells=False, transform=False) self.handler.write_block(new_vals) QApplication.restoreOverrideCursor() self.raster.triggerRepaint() def apply_spin_box_values(self): if not self.selection_tool.selected_geometries: return self.apply_values(self.spin_boxes.get_values()) def apply_nodata_value(self): if not self.selection_tool.selected_geometries: return self.apply_values(self.handler.nodata_values) def apply_low_pass_filter(self): QApplication.setOverrideCursor(Qt.WaitCursor) self.handler.select(self.selection_tool.selected_geometries, all_touched_cells=self.all_touched) self.handler.write_block(low_pass_filter=True) QApplication.restoreOverrideCursor() self.raster.triggerRepaint() def clear_selection(self): if self.selection_tool: self.selection_tool.clear_all_selections() def selection_from_layer(self): """Create a new selection from layer.""" self.selection_tool.init_tool(self.raster, mode=self.POLYGON_SELECTION, line_width=self.get_cur_line_width()) dlg = LayerSelectDialog() if not dlg.exec_(): return cur_layer = dlg.cbo.currentLayer() if not cur_layer.type() == QgsMapLayerType.VectorLayer: return self.selection_tool.selection_from_layer(cur_layer) def selection_to_layer(self): """Create a memory layer from current selection""" geoms = self.selection_tool.selected_geometries if geoms is None or not self.raster: return crs_str = self.raster.crs().toProj() nr = self.selection_layers_count self.selection_layers_count += 1 mlayer = QgsVectorLayer(f"Polygon?crs={crs_str}&field=fid:int", f"Raster selection {nr}", "memory") fields = mlayer.dataProvider().fields() features = [] for i, geom in enumerate(geoms): feat = QgsFeature(fields) feat["fid"] = i + 1 feat.setGeometry(geom) features.append(feat) mlayer.dataProvider().addFeatures(features) self.project.addMapLayer(mlayer) def toggle_all_touched(self): """Toggle selection mode.""" # button is toggled automatically when clicked, just update the attribute self.all_touched = self.toggle_all_touched_btn.isChecked() def point_clicked(self, point=None, button=None): if self.raster is None: self.uc.bar_warn("Choose a raster to work with...", dur=3) return if self.logger: self.logger.debug(f"Clicked point in canvas CRS: {point if point else self.last_point}") if point is None: ptxy_in_src_crs = self.last_point else: if self.crs_transform: if self.logger: self.logger.debug(f"Transforming clicked point {point}") try: ptxy_in_src_crs = self.crs_transform.transform(point) except QgsCsException as err: self.uc.show_warn( "Point coordinates transformation failed! Check the raster projection:\n\n{}".format(repr(err))) return else: ptxy_in_src_crs = QgsPointXY(point.x(), point.y()) if self.logger: self.logger.debug(f"Clicked point in raster CRS: {ptxy_in_src_crs}") self.last_point = ptxy_in_src_crs ident_vals = self.handler.provider.identify(ptxy_in_src_crs, QgsRaster.IdentifyFormatValue).results() cur_vals = list(ident_vals.values()) # check if the point is within active raster extent if not self.rbounds[0] <= ptxy_in_src_crs.x() <= self.rbounds[2]: self.uc.bar_info("Out of x bounds", dur=3) return if not self.rbounds[1] <= ptxy_in_src_crs.y() <= self.rbounds[3]: self.uc.bar_info("Out of y bounds", dur=3) return if self.mode == 'draw': new_vals = self.spin_boxes.get_values() if self.logger: self.logger.debug(f"Applying const value {new_vals}") self.apply_values_single_cell(new_vals) else: self.spin_boxes.set_values(cur_vals) if 2 < self.handler.bands_nr < 5: self.color_picker_connection(connect=False) self.color_btn.setColor(QColor(*self.spin_boxes.get_values()[:4])) self.color_picker_connection(connect=True) def set_values_from_picker(self, c): """Set bands spinboxes values after color change in the color picker""" values = None if self.handler.bands_nr > 2: values = [c.red(), c.green(), c.blue()] if self.handler.bands_nr == 4: values.append(c.alpha()) if values: self.spin_boxes.set_values(values) def set_nodata(self): """Set NoData value(s) for each band of current raster.""" if not self.raster: self.uc.bar_warn('Select a raster layer to define/change NoData value!') return if self.handler.provider.userNoDataValues(1): note = '\nNote: there is a user defined NODATA value.\nCheck the raster properties (Transparency).' else: note = '' dt = self.handler.provider.dataType(1) # current NODATA value if self.handler.provider.sourceHasNoDataValue(1): cur_nodata = self.handler.provider.sourceNoDataValue(1) if dt < 6: cur_nodata = '{0:d}'.format(int(float(cur_nodata))) else: cur_nodata = '' label = 'Define/change raster NODATA value.\n\n' label += 'Raster src_data type: {}.{}'.format(dtypes[dt]['name'], note) nd, ok = QInputDialog.getText(None, "Define NODATA Value", label, QLineEdit.Normal, str(cur_nodata)) if not ok: return if not is_number(nd): self.uc.show_warn('Wrong NODATA value!') return new_nodata = int(nd) if dt < 6 else float(nd) # set the NODATA value for each band res = [] for nr in self.handler.bands_range: res.append(self.handler.provider.setNoDataValue(nr, new_nodata)) self.handler.provider.sourceHasNoDataValue(nr) if False in res: self.uc.show_warn('Setting new NODATA value failed!') else: self.uc.bar_info('Successful setting new NODATA values!', dur=2) self.set_active_raster() self.raster.triggerRepaint() def check_undo_redo_btns(self): """Enable/Disable undo and redo buttons based on availability of undo/redo for current raster.""" self.undo_btn.setDisabled(True) self.redo_btn.setDisabled(True) if self.raster is None or self.raster.id() not in self.changes: return changes = self.changes[self.raster.id()] if changes.nr_undos() > 0: self.undo_btn.setEnabled(True) if changes.nr_redos() > 0: self.redo_btn.setEnabled(True) def enable_toolbar_actions(self, enable=True): """Enable / disable all toolbar actions but Help (for vectors and unsupported rasters)""" for widget in self.actions + [self.width_unit_cbo, self.line_width_sbox]: widget.setEnabled(enable) if widget in self.actions_always_on: widget.setEnabled(True) self.spin_boxes.enable(enable) @staticmethod def check_layer(layer): """Check if we can work with the raster""" if layer is None: return False if layer.type() != QgsMapLayerType.RasterLayer: return False if layer.providerType() != 'gdal': return False if all([ layer.isValid(), layer.crs() is not None, check_gdal_driver_create_option(layer), # GDAL driver has CREATE option os.path.isfile(layer.dataProvider().dataSourceUri()), # is it a local file? ]): return True else: return False def set_bands_cbo(self): self.bands_cbo.currentIndexChanged.disconnect(self.update_active_bands) self.bands_cbo.clear() for band in self.handler.bands_range: self.bands_cbo.addItem(f"{band}", [band]) if self.handler.bands_nr > 1: self.bands_cbo.addItem(self.RGB, [1, 2, 3]) self.bands_cbo.setCurrentIndex(0) self.bands_cbo.currentIndexChanged.connect(self.update_active_bands) def update_active_bands(self, idx): bands = self.bands_cbo.currentData() self.handler.active_bands = bands self.spin_boxes.create_spinboxes(bands, self.handler.data_types, self.handler.nodata_values) self.color_btn.setEnabled(len(bands) > 1) self.exp_dlg_btn.setEnabled(len(bands) == 1) def set_active_raster(self): """Active layer has changed - check if it is a raster layer and prepare it for the plugin""" old_spin_boxes_values = self.spin_boxes.get_values() self.crs_transform = None layer = self.iface.activeLayer() if self.check_layer(layer): self.raster = layer self.crs_transform = None if self.project.crs() == self.raster.crs() else \ QgsCoordinateTransform(self.project.crs(), self.raster.crs(), self.project) self.handler = RasterHandler(self.raster, self.uc, self.debug) supported, unsupported_type = self.handler.write_supported() if supported: self.enable_toolbar_actions() self.set_bands_cbo() self.spin_boxes.create_spinboxes(self.handler.active_bands, self.handler.data_types, self.handler.nodata_values) if self.handler.bands_nr == len(old_spin_boxes_values): self.spin_boxes.set_values(old_spin_boxes_values) self.bands_cbo.setEnabled(self.handler.bands_nr > 1) self.color_btn.setEnabled(len(self.handler.active_bands) > 1) self.rbounds = self.raster.extent().toRectF().getCoords() self.handler.raster_changed.connect(self.add_to_undo) if self.raster.id() not in self.changes: self.changes[self.raster.id()] = RasterChanges(nr_to_keep=self.settings["undo_steps"]) else: msg = f"The raster has unsupported src_data type: {unsupported_type}" msg += "\nServal can't work with it, sorry..." self.uc.show_warn(msg) self.enable_toolbar_actions(enable=False) self.reset_raster() else: # unsupported raster self.enable_toolbar_actions(enable=False) self.reset_raster() self.check_undo_redo_btns() def add_to_undo(self, change): """Add the old and new blocks to undo stack.""" self.changes[self.raster.id()].add_change(change) self.check_undo_redo_btns() if self.logger: self.logger.debug(self.get_undo_redo_values()) def get_undo_redo_values(self): changes = self.changes[self.raster.id()] return f"nr undos: {changes.nr_undos()}, redos: {changes.nr_redos()}" def undo(self): undo_data = self.changes[self.raster.id()].undo() self.handler.write_block_undo(undo_data) self.raster.triggerRepaint() self.check_undo_redo_btns() def redo(self): redo_data = self.changes[self.raster.id()].redo() self.handler.write_block_undo(redo_data) self.raster.triggerRepaint() self.check_undo_redo_btns() def reset_raster(self): self.raster = None self.color_btn.setDisabled(True) def color_picker_connection(self, connect=True): if connect: self.color_btn.colorChanged.connect(self.set_values_from_picker) else: self.color_btn.colorChanged.disconnect(self.set_values_from_picker) @staticmethod def show_website(): QDesktopServices.openUrl(QUrl("https://github.com/lutraconsulting/serval/blob/master/Serval/docs/user_manual.md")) def recreate_spatial_index(self, layer): """Check if spatial index exists for the layer and if it is relatively old and eventually recreate it.""" ctime = self.spatial_index_time[layer.id()] if layer.id() in self.spatial_index_time else None if ctime is None or datetime.now() - ctime > timedelta(seconds=30): self.spatial_index = QgsSpatialIndex(layer.getFeatures(), None, QgsSpatialIndex.FlagStoreFeatureGeometries) self.spatial_index_time[layer.id()] = datetime.now() def get_nearest_feature(self, pt_feat, vlayer_id): """Given the point feature, return nearest feature from vlayer.""" vlayer = self.project.mapLayer(vlayer_id) self.recreate_spatial_index(vlayer) ptxy = pt_feat.geometry().asPoint() near_fid = self.spatial_index.nearestNeighbor(ptxy)[0] return vlayer.getFeature(near_fid) def nearest_feature_attr_value(self, pt_feat, vlayer_id, attr_name): """Find nearest feature to pt_feat and return its attr_name attribute value.""" near_feat = self.get_nearest_feature(pt_feat, vlayer_id) return near_feat[attr_name] def nearest_pt_on_line_interpolate_z(self, pt_feat, vlayer_id): """Find nearest line feature to pt_feat and interpolate z value from vertices.""" near_feat = self.get_nearest_feature(pt_feat, vlayer_id) near_geom = near_feat.geometry() closest_pt_dist = near_geom.lineLocatePoint(pt_feat.geometry()) closest_pt = near_geom.interpolate(closest_pt_dist) return closest_pt.get().z() def intersecting_features_attr_average(self, pt_feat, vlayer_id, attr_name, only_center): """ Find all features intersecting current feature (cell center, or raster cell polygon) and calculate average value of their attr_name attribute. """ vlayer = self.project.mapLayer(vlayer_id) self.recreate_spatial_index(vlayer) ptxy = pt_feat.geometry().asPoint() pt_x, pt_y = ptxy.x(), ptxy.y() dxy = 0.001 half_pix_x = self.handler.pixel_size_x / 2. half_pix_y = self.handler.pixel_size_y / 2. if only_center: cell = QgsRectangle(pt_x, pt_y, pt_x + dxy, pt_y + dxy) else: cell = QgsRectangle(pt_x - half_pix_x, pt_y - half_pix_y, pt_x + half_pix_x, pt_y + half_pix_y) inter_fids = self.spatial_index.intersects(cell) values = [] for fid in inter_fids: feat = vlayer.getFeature(fid) if not feat.geometry().intersects(cell): continue val = feat[attr_name] if not is_number(val): continue values.append(val) if len(values) == 0: return None return sum(values) / float(len(values)) def interpolate_from_mesh(self, pt_feat, mesh_layer_id, group, dataset, above_existing): """Interpolate from mesh.""" mesh_layer = self.project.mapLayer(mesh_layer_id) ptxy = pt_feat.geometry().asPoint() dataset_val = mesh_layer.datasetValue(QgsMeshDatasetIndex(group, dataset), ptxy) val = dataset_val.scalar() if math.isnan(val): return val if above_existing: ident_vals = self.handler.provider.identify(ptxy, QgsRaster.IdentifyFormatValue).results() org_val = list(ident_vals.values())[0] if org_val == self.handler.nodata_values[0]: return val return max(org_val, val) else: return val