def testSignals(self): QgsProject.instance().clear() m = QgsMapLayerComboBox() l1 = QgsVectorLayer( "Point?crs=EPSG:3111&field=fldtxt:string&field=fldint:integer", 'layer 1', "memory") QgsProject.instance().addMapLayer(l1) l2 = QgsVectorLayer( "Polygon?crs=EPSG:3111&field=fldtxt:string&field=fldint:integer", 'lAyEr 2', "memory") QgsProject.instance().addMapLayer(l2) spy = QSignalSpy(m.layerChanged) m.setLayer(l2) self.assertEqual(len(spy), 1) self.assertEqual(m.currentLayer(), l2) m.setLayer(l1) self.assertEqual(len(spy), 2) self.assertEqual(m.currentLayer(), l1) # no signal if same layer m.setLayer(l1) self.assertEqual(len(spy), 2) m.setAllowEmptyLayer(True) m.setLayer(None) self.assertEqual(len(spy), 3) self.assertIsNone(m.currentLayer()) m.setLayer(None) self.assertEqual(len(spy), 3) self.assertIsNone(m.currentLayer()) m.setLayer(l1) self.assertEqual(len(spy), 4) self.assertEqual(m.currentLayer(), l1)
class OverlapDialog(QDialog, FORM_CLASS): def __init__(self, parent=None, iface=None): """Constructor.""" super(OverlapDialog, self).__init__(parent) # Set up the user interface from Designer through FORM_CLASS. # After self.setupUi() you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect QDialog.__init__(self, parent) self.parent = parent self.iface = iface self.setupUi(self) # layer self.layer = QgsMapLayerComboBox(parent) self.layer.setFilters(QgsMapLayerProxyModel.PolygonLayer) # self.layer.layerChanged.connect(self.check_form_validation) # output self.result.setStorageMode(storageMode=QgsFileWidget.SaveFile) self.result.setDialogTitle("Save File") # Set up things for ok button self.ok_button = self.button_ovelap.button(QDialogButtonBox.Ok) # self.ok_button.setEnabled(False) self.ok_button.clicked.connect(self.accept) # Set up things for cancel button self.cancel_button = self.button_ovelap.button(QDialogButtonBox.Cancel) self.cancel_button.clicked.connect(self.reject) def check_form_validation(self): """Slot for in form modif""" input_layer = self.layer.currentLayer() if input_layer is None: self.ok_button.setEnabled(True) else: self.ok_button.setEnabled(False) def accept(self): """Method invoked when OK button is clicked.""" input_layer = self.layer.currentLayer() out = self.result.filePath() file_out = QgsFileWidget.splitFilePaths(out) count_overlap_action(input_layer, out) QgsMessageLog.logMessage( "Input layer: {} and Output: {}".format(input_layer.name(), file_out), 'Footprint', Qgis.Info) self.close()
class LayerSelectionDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle(self.tr('Select Extent')) vl = QVBoxLayout() vl.addWidget(QLabel(self.tr('Use extent from'))) self.combo = QgsMapLayerComboBox() self.combo.setFilters(QgsMapLayerProxyModel.HasGeometry | QgsMapLayerProxyModel.RasterLayer | QgsMapLayerProxyModel.MeshLayer) self.combo.setShowCrs( ProcessingConfig.getSetting(ProcessingConfig.SHOW_CRS_DEF)) vl.addWidget(self.combo) self.button_box = QDialogButtonBox() self.button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) vl.addWidget(self.button_box) self.setLayout(vl) def selected_layer(self): return self.combo.currentLayer()
class SingleLayerSelectDialog(QDialog): def __init__(self, parent=None, label=""): QDialog.__init__(self, parent) vl = QVBoxLayout() if label: vl.addWidget(QLabel(label)) self.comboBox = QgsMapLayerComboBox() self.comboBox.setFilters(QgsMapLayerProxyModel.HasGeometry | QgsMapLayerProxyModel.RasterLayer | QgsMapLayerProxyModel.MeshLayer) vl.addWidget(self.comboBox) self.buttonBox = QDialogButtonBox() self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) vl.addWidget(self.buttonBox) self.setLayout(vl) def selectedLayer(self): return self.comboBox.currentLayer()
def testProject(self): QgsProject.instance().clear() lA = create_layer('lA') lB = create_layer('lB') projectA = QgsProject.instance() projectB = QgsProject() projectA.addMapLayer(lA) projectB.addMapLayer(lB) cb = QgsMapLayerComboBox() self.assertEqual(cb.currentLayer(), lA) cb.setProject(projectB) self.assertEqual(cb.currentLayer(), lB) cb.setProject(projectA) self.assertEqual(cb.currentLayer(), lA) QgsProject.instance().clear()
class load_layer_dialog(QDialog): def __init__(self, parent=None, routes=[]): super(QDialog, self).__init__(parent) self.setModal(True) #modal blocks other stuff self.layer_box = QgsMapLayerComboBox() self.ok_button = QtGui.QPushButton('Ok') self.cancel_button = QtGui.QPushButton('Cancel') layout = QtGui.QHBoxLayout(self) layout.addWidget(self.layer_box) layout.addWidget(self.ok_button) layout.addWidget(self.cancel_button) self.routes = routes self.ok_button.clicked.connect(self.load) self.cancel_button.clicked.connect(self.reject) def load(self): load_layer(self.layer_box.currentLayer(), self.routes) self.accept()
class LayerSelectionDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle(self.tr('Select Extent')) vl = QVBoxLayout() vl.addWidget(QLabel(self.tr('Use extent from'))) self.combo = QgsMapLayerComboBox() self.combo.setFilters( QgsMapLayerProxyModel.HasGeometry | QgsMapLayerProxyModel.RasterLayer | QgsMapLayerProxyModel.MeshLayer) self.combo.setShowCrs(ProcessingConfig.getSetting(ProcessingConfig.SHOW_CRS_DEF)) vl.addWidget(self.combo) self.button_box = QDialogButtonBox() self.button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) vl.addWidget(self.button_box) self.setLayout(vl) def selected_layer(self): return self.combo.currentLayer()
class CatchmentsModule(QDockWidget, FORM_CLASS): def __init__(self, parent, parents=None): super(CatchmentsModule, self).__init__(parents) self.setupUi(self) self.parent = parent self.iface = parent.iface self.fillDialog() def fillDialog(self): self.layerComboBox = QgsMapLayerComboBox(self) self.layerComboBox.setObjectName('layerComboBox') self.layerComboBox.setFilters(QgsMapLayerProxyModel.PointLayer) self.layersLayout.addWidget(self.layerComboBox) self.providersComboBox.addItems(['Skobbler', 'HERE']) self.modesComboBox.addItems(['Car', 'Bike', 'Pedestrian']) self.unitsComboBox.addItems(['Minutes', 'Meters']) self.valueSpinBox.setMinimum(1) self.valueSpinBox.setMaximum(99999) self.valueSpinBox.setValue(10) self.getCatchments.setEnabled(False) self.getKeyLabel.setText('<html><head/><body><p>\ <a href="https://developer.skobbler.com/getting-started/web#sec3">\ <span style=" text-decoration: underline; color:#0000ff;">Get key</span></a></p></body></html>' ) self.connectFunctions() self.loadKey() def connectFunctions(self): self.providersComboBox.currentIndexChanged.connect(self.changeProvider) self.keyLineEdit.textChanged.connect(self.saveKey) self.layerComboBox.currentIndexChanged.connect(self.changeLayerEvent) self.modesComboBox.currentIndexChanged.connect(self.disableUnnecessaryParams) self.selectCheckBox.stateChanged.connect(self.updateFeaturesQuantity) self.getCatchments.clicked.connect(self.run) def disableUnnecessaryParams(self): if self.modesComboBox.currentText() == 'Pedestrian': self.trafficCheckBox.setEnabled(False) self.highwaysCheckBox.setEnabled(False) self.tollsCheckBox.setEnabled(False) self.highwaysCheckBox.setChecked(False) self.tollsCheckBox.setChecked(False) self.trafficCheckBox.setChecked(False) elif self.providersComboBox.currentText() == 'Skobbler': self.trafficCheckBox.setEnabled(False) self.highwaysCheckBox.setEnabled(True) self.tollsCheckBox.setEnabled(True) elif self.providersComboBox.currentText() == 'HERE': self.trafficCheckBox.setEnabled(True) self.highwaysCheckBox.setEnabled(False) self.tollsCheckBox.setEnabled(False) def changeProvider(self): self.modesComboBox.clear() if self.providersComboBox.currentText() == 'Skobbler': items = ['Car', 'Bike', 'Pedestrian'] self.highwaysCheckBox.setEnabled(True) self.tollsCheckBox.setEnabled(True) self.trafficCheckBox.setEnabled(False) self.trafficCheckBox.setChecked(False) self.getKeyLabel.setText('<html><head/><body><p>\ <a href="https://developer.skobbler.com/apikeys">\ <span style=" text-decoration: underline; color:#0000ff;">Get key</span></a></p></body></html>' ) self.keyLineEdit.setPlaceholderText('Insert Api Code') elif self.providersComboBox.currentText() == 'HERE': items = ['Car', 'Pedestrian', 'Truck'] self.trafficCheckBox.setEnabled(True) self.highwaysCheckBox.setEnabled(False) self.highwaysCheckBox.setChecked(False) self.tollsCheckBox.setChecked(False) self.tollsCheckBox.setEnabled(False) self.getKeyLabel.setText('<html><head/><body><p>\ <a href="https://developer.here.com/?create=Evaluation&keepState=true&step=account">\ <span style=" text-decoration: underline; color:#0000ff;">Get key</span></a></p></body></html>' ) self.keyLineEdit.setPlaceholderText('Insert App ID and App Code separated by \':\'') self.modesComboBox.addItems(items) self.loadKey() def saveKey(self): value = 'gissupport/location_lab/{}'.format(self.providersComboBox.currentText()) QSettings().setValue(value, self.keyLineEdit.text()) def loadKey(self): value = 'gissupport/location_lab/{}'.format(self.providersComboBox.currentText()) self.keyLineEdit.setText(QSettings().value(value) or '') def getPoints(self, vl, features): trans = QgsCoordinateTransform(vl.crs(), QgsCoordinateReferenceSystem(4326)) points = [] for f in features: geom = f.geometry() geom.transform(trans) if geom.isMultipart(): points.append(geom.asMultiPoint()[0]) else: points.append(geom.asPoint()) return points def requestApi(self, points): polygons = [] not_found = 0 if self.providersComboBox.currentText() == 'Skobbler': """ Skobbler options: start string Center of RealReach™ in GPS coordinates: Latitude, Longitude transport string You can pick one of the transport options: pedestrian, bike, car range int The range for which we calculate RealReach™ units string You can choose between sec and meter. 'Sec' is for time and 'Meter' is for distance toll boolean You can specify whether to avoid or not the use of toll roads in route calculation highways boolean Specifies whether to avoid or not the use of highways in route calculation """ for p in points: params = { 'source': self.providersComboBox.currentText(), 'url': 'tor.skobbler.net/tor/RSngx/RealReach/json/20_5/en', 'key': self.keyLineEdit.text().strip(), 'start': '{x},{y}'.format(x=p[1], y=p[0]), 'transport': self.modesComboBox.currentText().lower(), 'range': self.valueSpinBox.value() if self.unitsComboBox.currentText() == 'Meters' else self.valueSpinBox.value() * 60, 'units': 'meter' if self.unitsComboBox.currentText() == 'Meters' else 'sec', 'nonReachable': '0', 'toll': '1' if self.tollsCheckBox.isChecked() else '0', 'highways': '1' if self.highwaysCheckBox.isChecked() else '0', 'response_type': 'gps' } link = 'http://{key}.{url}/{key}\ ?start={start}\ &transport={transport}\ &range={range}\ &units={units}\ &nonReachable={nonReachable}\ &toll={toll}\ &highways={highways}\ &response_type={response_type}'.replace(' ', '').format(**params) try: r = urllib2.urlopen(link) except urllib2.HTTPError as e: return 'invalid key' continue data = json.loads(r.read()) if data['status']['apiMessage'] == 'Route cannot be calculated.': not_found += 1 continue params['coordinates'] = data['realReach']['gpsPoints'] polygons.append(params) elif self.providersComboBox.currentText() == 'HERE': """ HERE options: start string lat and lng mode string car, pedestrian or truck range int range for calculations rangetype string distance, time, consumption traffic boolean takes traffic """ for p in points: params = { 'source': self.providersComboBox.currentText(), 'url': 'http://isoline.route.cit.api.here.com/routing/7.2/calculateisoline.json', 'key': self.keyLineEdit.text().strip().split(':'), 'start': '{x},{y}'.format(x=p[1], y=p[0]), 'transport': HERE_PARAMS[self.modesComboBox.currentText()], 'range': self.valueSpinBox.value() if self.unitsComboBox.currentText() == 'Meters' else self.valueSpinBox.value() * 60, 'units': 'distance' if self.unitsComboBox.currentText() == 'Meters' else 'time', 'traffic': 'enabled' if self.trafficCheckBox.isChecked() else 'disabled' } link = '{url}\ ?app_id={key[0]}\ &app_code={key[1]}\ &mode=fastest;{transport};traffic:{traffic}\ &start=geo!{start}\ &range={range}\ &rangetype={units}'.replace(' ', '').format(**params) try: r = urllib2.urlopen(link) except urllib2.HTTPError as e: return 'invalid key' continue if r.getcode() == 403: return 'forbidden' params['coordinates'] = json.loads(r.read())['response']['isoline'][0]['component'][0]['shape'] polygons.append(params) if polygons and not_found: self.iface.messageBar().pushMessage( u'Catchments', u'{} catchments not found'.format(not_found), level=QgsMessageBar.INFO) return polygons def addPolygonsToMap(self, polygons): if not QgsMapLayerRegistry.instance().mapLayersByName('Location Lab - catchments'): vl = QgsVectorLayer('Polygon?crs=EPSG:4326', 'Location Lab - catchments', 'memory') pr = vl.dataProvider() vl.startEditing() pr.addAttributes( [ QgsField('id', QVariant.Int), QgsField('provider', QVariant.String), QgsField('mode', QVariant.String), QgsField('value', QVariant.Int), QgsField('units', QVariant.String), QgsField('lat', QVariant.Double), QgsField('lon', QVariant.Double), QgsField('params', QVariant.String) ] ) vl.commitChanges() QgsMapLayerRegistry.instance().addMapLayer(vl) vl = QgsMapLayerRegistry.instance().mapLayersByName('Location Lab - catchments')[0] pr = vl.dataProvider() next_id = len(vl.allFeatureIds()) + 1 for p in polygons: feature = QgsFeature() points = [] if p['source'] == 'Skobbler': coordinates_x = [c for c in p['coordinates'][8::2]] coordinates_y = [c for c in p['coordinates'][9::2]] for x, y in zip(coordinates_x, coordinates_y): points.append(QgsPoint(x, y)) elif p['source'] == 'HERE': coordinates = [c.split(',') for c in p['coordinates']] for xy in coordinates: points.append(QgsPoint(float(xy[1]), float(xy[0]))) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolygon([points])) lat, lon = p['start'].split(',') for key in ['key', 'url', 'coordinates', 'start']: #unnecessary params p.pop(key) feature.setAttributes([ next_id, self.providersComboBox.currentText(), self.modesComboBox.currentText().lower(), self.valueSpinBox.value(), self.unitsComboBox.currentText(), float(lat), float(lon), str(p) ]) pr.addFeatures([feature]) next_id += 1 vl.updateExtents() self.iface.mapCanvas().setExtent( QgsCoordinateTransform( vl.crs(), self.iface. mapCanvas(). mapRenderer(). destinationCrs()). transform(vl.extent())) self.iface.mapCanvas().refresh() def changeLayerEvent(self): vl = self.layerComboBox.currentLayer() if not vl: return self.updateFeaturesQuantity() vl.selectionChanged.connect(self.updateFeaturesQuantity) def updateFeaturesQuantity(self): vl = self.layerComboBox.currentLayer() if not vl: return if self.selectCheckBox.isChecked(): features = [f for f in vl.selectedFeatures()] else: features = [f for f in vl.getFeatures()] self.pointsLabel.setText('Number of points: {}'.format(len(features))) if len(features) > 5: self.getCatchments.setEnabled(False) self.pointsLabel.setText('Number of points: {} (limit is 5)'.format(len(features))) elif len(features) == 0: self.getCatchments.setEnabled(False) else: self.getCatchments.setEnabled(True) def show(self): self.changeLayerEvent() self.iface.addDockWidget(Qt.LeftDockWidgetArea, self) super(CatchmentsModule, self).show() def checkApiKey(self): if self.providersComboBox.currentText() == 'HERE': if len(self.keyLineEdit.text().split(':')) != 2: self.iface.messageBar().pushMessage( u'Catchments', u'Invalid api key format, required app_id:app_code', level=QgsMessageBar.WARNING) return False return True def run(self): vl = self.layerComboBox.currentLayer() if self.selectCheckBox.isChecked(): features = [f for f in vl.selectedFeatures()] else: features = [f for f in vl.getFeatures()] if not features: self.iface.messageBar().pushMessage( u'Catchments', u'No geometry', level=QgsMessageBar.WARNING) return points = self.getPoints(vl, features) if not self.checkApiKey(): return polygons = self.requestApi(points) if not polygons: self.iface.messageBar().pushMessage( u'Catchments', u'Catchments not found', level=QgsMessageBar.WARNING) return elif polygons == 'invalid key': self.iface.messageBar().pushMessage( u'Catchments', u'Invalid API key', level=QgsMessageBar.WARNING) return elif polygons == 'forbidden': self.iface.messageBar().pushMessage( u'Catchments', u'These credentials do not authorize access', level=QgsMessageBar.WARNING) return self.addPolygonsToMap(polygons)
class FieldMappingDialog(QDialog, FORM_CLASS): """Dialog implementation class for the InaSAFE field mapping tool.""" def __init__(self, parent=None, iface=None, setting=None): """Constructor.""" QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle(self.tr('InaSAFE Field Mapping Tool')) icon = resources_path('img', 'icons', 'show-mapping-tool.svg') self.setWindowIcon(QIcon(icon)) self.parent = parent self.iface = iface if setting is None: setting = QSettings() self.setting = setting self.keyword_io = KeywordIO() self.layer = None self.metadata = {} self.layer_input_layout = QHBoxLayout() self.layer_label = QLabel(tr('Layer')) self.layer_combo_box = QgsMapLayerComboBox() # Filter only for Polygon and Point self.layer_combo_box.setFilters( QgsMapLayerProxyModel.PolygonLayer | QgsMapLayerProxyModel.PointLayer) # Filter out a layer that don't have layer groups excepted_layers = [] for i in range(self.layer_combo_box.count()): layer = self.layer_combo_box.layer(i) try: keywords = self.keyword_io.read_keywords(layer) except (KeywordNotFoundError, NoKeywordsFoundError): excepted_layers.append(layer) continue layer_purpose = keywords.get('layer_purpose') if not layer_purpose: excepted_layers.append(layer) continue if layer_purpose == layer_purpose_exposure['key']: layer_subcategory = keywords.get('exposure') elif layer_purpose == layer_purpose_hazard['key']: layer_subcategory = keywords.get('hazard') else: layer_subcategory = None field_groups = get_field_groups(layer_purpose, layer_subcategory) if len(field_groups) == 0: excepted_layers.append(layer) continue self.layer_combo_box.setExceptedLayerList(excepted_layers) # Select the active layer. if self.iface.activeLayer(): found = self.layer_combo_box.findText( self.iface.activeLayer().name()) if found > -1: self.layer_combo_box.setLayer(self.iface.activeLayer()) self.field_mapping_widget = None self.main_stacked_widget.setCurrentIndex(1) # Input self.layer_input_layout.addWidget(self.layer_label) self.layer_input_layout.addWidget(self.layer_combo_box) self.header_label = QLabel() self.header_label.setWordWrap(True) self.main_layout.addWidget(self.header_label) self.main_layout.addLayout(self.layer_input_layout) # Signal self.layer_combo_box.layerChanged.connect(self.set_layer) if self.layer_combo_box.currentLayer(): self.set_layer(self.layer_combo_box.currentLayer()) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) # Set up things for ok button self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up things for cancel button self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.clicked.connect(self.reject) def set_layer(self, layer=None, keywords=None): """Set layer and update UI accordingly. :param layer: A QgsVectorLayer. :type layer: QgsVectorLayer :param keywords: Keywords for the layer. :type keywords: dict, None """ if self.field_mapping_widget is not None: self.field_mapping_widget.setParent(None) self.field_mapping_widget.close() self.field_mapping_widget.deleteLater() self.main_layout.removeWidget(self.field_mapping_widget) if layer: self.layer = layer else: self.layer = self.layer_combo_box.currentLayer() if not self.layer: return if keywords is not None: self.metadata = keywords else: # Always read from metadata file. try: self.metadata = self.keyword_io.read_keywords(self.layer) except ( NoKeywordsFoundError, KeywordNotFoundError, MetadataReadError) as e: raise e if 'inasafe_default_values' not in self.metadata: self.metadata['inasafe_default_values'] = {} if 'inasafe_fields' not in self.metadata: self.metadata['inasafe_fields'] = {} self.field_mapping_widget = FieldMappingWidget( parent=self, iface=self.iface) self.field_mapping_widget.set_layer(self.layer, self.metadata) self.field_mapping_widget.show() self.main_layout.addWidget(self.field_mapping_widget) # Set header label group_names = [ self.field_mapping_widget.tabText(i) for i in range( self.field_mapping_widget.count())] if len(group_names) == 0: header_text = tr( 'There is no field group for this layer. Please select ' 'another layer.') self.header_label.setText(header_text) return elif len(group_names) == 1: pretty_group_name = group_names[0] elif len(group_names) == 2: pretty_group_name = group_names[0] + tr(' and ') + group_names[1] else: pretty_group_name = ', '.join(group_names[:-1]) pretty_group_name += tr(', and {0}').format(group_names[-1]) header_text = tr( 'Please fill the information for every tab to determine the ' 'attribute for {0} group.').format(pretty_group_name) self.header_label.setText(header_text) @pyqtSlot() @pyqtSignature('bool') # prevents actions being handled twice def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2.1 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded: 3.2.1 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = field_mapping_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def save_metadata(self): """Save metadata based on the field mapping state.""" metadata = self.field_mapping_widget.get_field_mapping() for key, value in metadata['fields'].items(): # Delete the key if it's set to None if key in self.metadata['inasafe_default_values']: self.metadata['inasafe_default_values'].pop(key) if value is None or value == []: if key in self.metadata['inasafe_fields']: self.metadata['inasafe_fields'].pop(key) else: self.metadata['inasafe_fields'][key] = value for key, value in metadata['values'].items(): # Delete the key if it's set to None if key in self.metadata['inasafe_fields']: self.metadata['inasafe_fields'].pop(key) if value is None: if key in self.metadata['inasafe_default_values']: self.metadata['inasafe_default_values'].pop(key) else: self.metadata['inasafe_default_values'][key] = value # Save metadata try: self.keyword_io.write_keywords( layer=self.layer, keywords=self.metadata) except InaSAFEError, e: error_message = get_error_message(e) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QMessageBox.warning( self, self.tr('InaSAFE'), ((self.tr( 'An error was encountered when saving the following ' 'keywords:\n %s') % error_message.to_html()))) # Update setting fir recent value if self.metadata.get('inasafe_default_values'): for key, value in \ self.metadata['inasafe_default_values'].items(): set_inasafe_default_value_qsetting( self.setting, key, RECENT, value)
class PerimeterPuCaWidget(PuCaWidget): """A widget for 'perimeter' check.""" def _build_widgets(self): """Builds own widgets.""" self.lastPerimeterLayer = None self.perimeterMapLayerComboBox = QgsMapLayerComboBox(self) self.perimeterMapLayerComboBox.setObjectName( u'perimeterMapLayerComboBox') self.perimeterMapLayerComboBox.setFilters( QgsMapLayerProxyModel.PolygonLayer) self.perimeterMapLayerComboBox.activated.connect( self._sync_perimeter_map_layer_combo_box) QgsMapLayerRegistry.instance().layersAdded.connect( self._rollback_perimeter_layer) QgsMapLayerRegistry.instance().layersRemoved.connect( self._reset_perimeter_layer) self.set_perimeter_layer(self.lastPerimeterLayer) self.vBoxLayout.addWidget(self.perimeterMapLayerComboBox) def set_perimeter_layer(self, perimeterLayer, lastPerimeterLayer=True): """Sets the perimeter layer in the perimeterMapLayerComboBox. Args: perimeterLayer (QgsVectorLayer): A reference to the perimeter layer. lastPerimeterLayer (bool): True to set self.lastPerimeterLayer, False otherwise. """ if lastPerimeterLayer: self.lastPerimeterLayer = perimeterLayer self.perimeterMapLayerComboBox.setLayer(perimeterLayer) def _sync_perimeter_map_layer_combo_box(self): """Synchronizes perimeter map layer combo boxes. Synchronizes with the perimeterMapLayerComboBox in the editPuWidget. """ perimeterLayer = self.perimeterMapLayerComboBox.currentLayer() if perimeterLayer != self.lastPerimeterLayer: self.lastPerimeterLayer = perimeterLayer self.dW.stackedWidget.editPuWidget.set_perimeter_layer( perimeterLayer) def _reset_perimeter_layer(self): """Resets the perimeter layer.""" layers = self.iface.legendInterface().layers() if self.lastPerimeterLayer not in layers: self.set_perimeter_layer(None) def _rollback_perimeter_layer(self): """Rolls the perimeter layer back.""" if self.lastPerimeterLayer == None: self.set_perimeter_layer(self.lastPerimeterLayer, False) else: self.lastPerimeterLayer = \ self.perimeterMapLayerComboBox.currentLayer() def execute(self, layer): """Executes the check. Args: layer (QgsVectorLayer): A reference to the active layer. """ try: perimeterLayer = self.perimeterMapLayerComboBox.currentLayer() if not self.dW.check_perimeter_layer(perimeterLayer, layer, self.pW): return if perimeterLayer.featureCount() == 0: self.pW.set_text_statusbar.emit( u'Vrstva obvodu neobsahuje žádný prvek.', 10, True) return self.pW.set_text_statusbar.emit(u'Provádím kontrolu - obvodem...', 0, False) layer.removeSelection() perimeterLayer.removeSelection() processing.runalg('qgis:selectbylocation', layer, perimeterLayer, u'within', 0, 0) layer.invertSelection() featureCount = layer.selectedFeatureCount() duration = 10 warning = False if featureCount == 0: self.pW.set_text_statusbar.emit( u'Uvnitř obvodu jsou všechny parcely.', duration, warning) elif featureCount == 1: self.pW.set_text_statusbar.emit( u'Uvnitř obvodu není {} parcela'.format(featureCount), duration, warning) elif 1 < featureCount < 5: self.pW.set_text_statusbar.emit( u'Uvnitř obvodu nejsou {} parcely.'.format(featureCount), duration, warning) elif 5 <= featureCount: self.pW.set_text_statusbar.emit( u'Uvnitř obvodu není {} parcel.'.format(featureCount), duration, warning) except self.dW.puError: QgsApplication.processEvents() except: QgsApplication.processEvents() currentCheckAnalysisName = \ self.pW.checkAnalysisComboBox.currentText() self.dW.display_error_messages( self.pW, u'Error executing "{}".'.format(currentCheckAnalysisName), u'Chyba při provádění "{}".'.format(currentCheckAnalysisName))
class DSMGenerator: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'DSMGenerator_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = DSMGeneratorDialog() # Declare instance attributes self.actions = [] self.menu = self.tr(u'&DSM Generator') # TODO: We are going to let the user set this up in a future iteration # self.toolbar = self.iface.addToolBar(u'DSMGenerator') # self.toolbar.setObjectName(u'DSMGenerator') # Declare variables self.OSMoutputfile = None self.DSMoutputfile = None if not (os.path.isdir(self.plugin_dir + '/temp')): os.mkdir(self.plugin_dir + '/temp') # Access the raster layer self.layerComboManagerDEM = QgsMapLayerComboBox(self.dlg.widgetRaster) self.layerComboManagerDEM.setFilters(QgsMapLayerProxyModel.RasterLayer) self.layerComboManagerDEM.setFixedWidth(175) self.layerComboManagerDEM.setCurrentIndex(-1) # Access the vector layer and an attribute field self.layerComboManagerPolygon = QgsMapLayerComboBox( self.dlg.widgetPolygon) self.layerComboManagerPolygon.setCurrentIndex(-1) self.layerComboManagerPolygon.setFilters( QgsMapLayerProxyModel.PolygonLayer) self.layerComboManagerPolygon.setFixedWidth(175) self.layerComboManagerPolygonField = QgsFieldComboBox( self.dlg.widgetField) self.layerComboManagerPolygonField.setFilters( QgsFieldProxyModel.Numeric) self.layerComboManagerPolygonField.setFixedWidth(150) self.layerComboManagerPolygon.layerChanged.connect( self.layerComboManagerPolygonField.setLayer) # Set up of DSM file save dialog self.DSMfileDialog = QFileDialog() self.dlg.saveButton.clicked.connect(self.savedsmfile) # Set up of OSM polygon file save dialog self.OSMfileDialog = QFileDialog() self.dlg.savePolygon.clicked.connect(self.saveosmfile) # Set up for the Help button self.dlg.helpButton.clicked.connect(self.help) # Set up for the Close button self.dlg.closeButton.clicked.connect(self.resetPlugin) # Set up for the Run button self.dlg.runButton.clicked.connect(self.start_progress) # Set up extent self.dlg.canvasButton.toggled.connect(self.checkbox_canvas) # self.dlg.layerButton.toggled.connect(self.checkbox_layer) self.layerComboManagerExtent = QgsMapLayerComboBox( self.dlg.widgetLayerExtent) self.layerComboManagerExtent.setCurrentIndex(-1) self.layerComboManagerExtent.layerChanged.connect(self.checkbox_layer) self.layerComboManagerExtent.setFixedWidth(175) # noinspection PyMethodMayBeStatic def tr(self, message): return QCoreApplication.translate('DSMGenerator', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): icon = QIcon(icon_path) 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 initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/DSMGenerator/icon.png' self.add_action(icon_path, text=self.tr(u'DSM Generator'), callback=self.run, parent=self.iface.mainWindow()) def savedsmfile(self): self.DSMoutputfile = self.DSMfileDialog.getSaveFileName( None, "Save File As:", None, "Raster Files (*.tif)") self.dlg.DSMtextOutput.setText(self.DSMoutputfile) def saveosmfile(self): self.OSMoutputfile = self.OSMfileDialog.getSaveFileName( None, "Save File As:", None, "Shapefiles (*.shp)") self.dlg.OSMtextOutput.setText(self.OSMoutputfile) def checkbox_canvas(self): extent = self.iface.mapCanvas().extent() self.dlg.lineEditNorth.setText(str(extent.yMaximum())) self.dlg.lineEditSouth.setText(str(extent.yMinimum())) self.dlg.lineEditWest.setText(str(extent.xMinimum())) self.dlg.lineEditEast.setText(str(extent.xMaximum())) def checkbox_layer(self): dem_layer_extent = self.layerComboManagerExtent.currentLayer() if dem_layer_extent: extent = dem_layer_extent.extent() self.dlg.lineEditNorth.setText(str(extent.yMaximum())) self.dlg.lineEditSouth.setText(str(extent.yMinimum())) self.dlg.lineEditWest.setText(str(extent.xMinimum())) self.dlg.lineEditEast.setText(str(extent.xMaximum())) # Help button def help(self): url = "http://umep-docs.readthedocs.io/en/latest/pre-processor/Spatial%20Data%20DSM%20Generator.html" webbrowser.open_new_tab(url) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u'&DSM Generator'), action) # self.iface.removeToolBarIcon(action) # remove the toolbar # del self.toolbar def run(self): self.dlg.show() self.dlg.exec_() def start_progress(self): import datetime start = datetime.datetime.now() # Check OS and dep if sys.platform == 'darwin': gdal_os_dep = '/Library/Frameworks/GDAL.framework/Versions/Current/Programs/' else: gdal_os_dep = '' if self.dlg.canvasButton.isChecked(): # Map Canvas extentCanvasCRS = self.iface.mapCanvas() srs = extentCanvasCRS.mapSettings().destinationCrs() crs = str(srs.authid()) # old_crs = osr.SpatialReference() # old_crs.ImportFromEPSG(int(crs[5:])) can_crs = QgsCoordinateReferenceSystem(int(crs[5:])) # can_wkt = extentCanvasCRS.mapRenderer().destinationCrs().toWkt() # can_crs = osr.SpatialReference() # can_crs.ImportFromWkt(can_wkt) # Raster Layer dem_layer = self.layerComboManagerDEM.currentLayer() dem_prov = dem_layer.dataProvider() dem_path = str(dem_prov.dataSourceUri()) dem_raster = gdal.Open(dem_path) projdsm = osr.SpatialReference(wkt=dem_raster.GetProjection()) projdsm.AutoIdentifyEPSG() projdsmepsg = int(projdsm.GetAttrValue('AUTHORITY', 1)) dem_crs = QgsCoordinateReferenceSystem(projdsmepsg) # dem_wkt = dem_raster.GetProjection() # dem_crs = osr.SpatialReference() # dem_crs.ImportFromWkt(dem_wkt) if can_crs != dem_crs: extentCanvas = self.iface.mapCanvas().extent() extentDEM = dem_layer.extent() transformExt = QgsCoordinateTransform(can_crs, dem_crs) # transformExt = osr.CoordinateTransformation(can_crs, dem_crs) canminx = extentCanvas.xMinimum() canmaxx = extentCanvas.xMaximum() canminy = extentCanvas.yMinimum() canmaxy = extentCanvas.yMaximum() canxymin = transformExt.TransformPoint(canminx, canminy) canxymax = transformExt.TransformPoint(canmaxx, canmaxy) extDiffminx = canxymin[0] - extentDEM.xMinimum( ) # If smaller than zero = warning extDiffminy = canxymin[1] - extentDEM.yMinimum( ) # If smaller than zero = warning extDiffmaxx = canxymax[0] - extentDEM.xMaximum( ) # If larger than zero = warning extDiffmaxy = canxymax[0] - extentDEM.yMaximum( ) # If larger than zero = warning if extDiffminx < 0 or extDiffminy < 0 or extDiffmaxx > 0 or extDiffmaxy > 0: QMessageBox.warning( None, "Warning! Extent of map canvas is larger than raster extent.", "Change to an extent equal to or smaller than the raster extent." ) return # Extent self.yMax = self.dlg.lineEditNorth.text() self.yMin = self.dlg.lineEditSouth.text() self.xMin = self.dlg.lineEditWest.text() self.xMax = self.dlg.lineEditEast.text() if not self.DSMoutputfile: QMessageBox.critical(None, "Error", "Specify a raster output file") return if self.dlg.checkBoxPolygon.isChecked() and not self.OSMoutputfile: QMessageBox.critical(None, "Error", "Specify an output file for OSM data") return # Acquiring geodata and attributes dem_layer = self.layerComboManagerDEM.currentLayer() if dem_layer is None: QMessageBox.critical(None, "Error", "No valid raster layer is selected") return else: provider = dem_layer.dataProvider() filepath_dem = str(provider.dataSourceUri()) demRaster = gdal.Open(filepath_dem) dem_layer_crs = osr.SpatialReference() dem_layer_crs.ImportFromWkt(demRaster.GetProjection()) self.dem_layer_unit = dem_layer_crs.GetAttrValue("UNIT") posUnits = [ 'metre', 'US survey foot', 'meter', 'm', 'ft', 'feet', 'foot', 'ftUS', 'International foot' ] # Possible units if not self.dem_layer_unit in posUnits: QMessageBox.critical( None, "Error", "Raster projection is not in metre or foot. Please reproject.") return polygon_layer = self.layerComboManagerPolygon.currentLayer() osm_layer = self.dlg.checkBoxOSM.isChecked() if polygon_layer is None and osm_layer is False: QMessageBox.critical(None, "Error", "No valid building height layer is selected") return elif polygon_layer: vlayer = QgsVectorLayer(polygon_layer.source(), "buildings", "ogr") fileInfo = QFileInfo(polygon_layer.source()) polygon_ln = fileInfo.baseName() polygon_field = self.layerComboManagerPolygonField.currentField() idx = vlayer.fieldNameIndex(polygon_field) flname = vlayer.attributeDisplayName(idx) if idx == -1: QMessageBox.critical( None, "Error", "An attribute with unique fields must be selected") return ### main code ### self.dlg.progressBar.setRange(0, 5) self.dlg.progressBar.setValue(1) if self.dlg.checkBoxOSM.isChecked(): # TODO replace osr.CoordinateTransformation with QgsCoordinateTransform dem_original = gdal.Open(filepath_dem) dem_wkt = dem_original.GetProjection() ras_crs = osr.SpatialReference() ras_crs.ImportFromWkt(dem_wkt) rasEPSG = ras_crs.GetAttrValue("PROJCS|AUTHORITY", 1) if self.dlg.layerButton.isChecked(): old_crs = ras_crs elif self.dlg.canvasButton.isChecked(): canvasCRS = self.iface.mapCanvas() outputWkt = canvasCRS.mapRenderer().destinationCrs().toWkt() old_crs = osr.SpatialReference() old_crs.ImportFromWkt(outputWkt) wgs84_wkt = """ GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.257223563, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4326"]]""" new_crs = osr.SpatialReference() new_crs.ImportFromWkt(wgs84_wkt) transform = osr.CoordinateTransformation(old_crs, new_crs) minx = float(self.xMin) miny = float(self.yMin) maxx = float(self.xMax) maxy = float(self.yMax) lonlatmin = transform.TransformPoint(minx, miny) lonlatmax = transform.TransformPoint(maxx, maxy) if ras_crs != old_crs: rasTrans = osr.CoordinateTransformation(old_crs, ras_crs) raslonlatmin = rasTrans.TransformPoint(float(self.xMin), float(self.yMin)) raslonlatmax = rasTrans.TransformPoint(float(self.xMax), float(self.yMax)) #else: #raslonlatmin = [float(self.xMin), float(self.yMin)] #raslonlatmax = [float(self.xMax), float(self.yMax)] self.xMin = raslonlatmin[0] self.yMin = raslonlatmin[1] self.xMax = raslonlatmax[0] self.yMax = raslonlatmax[1] # Make data queries to overpass-api urlStr = 'http://overpass-api.de/api/map?bbox=' + str( lonlatmin[0]) + ',' + str(lonlatmin[1]) + ',' + str( lonlatmax[0]) + ',' + str(lonlatmax[1]) osmXml = urllib.urlopen(urlStr).read() #print urlStr # Make OSM building file osmPath = self.plugin_dir + '/temp/OSM_building.osm' osmFile = open(osmPath, 'w') osmFile.write(osmXml) if os.fstat(osmFile.fileno()).st_size < 1: urlStr = 'http://api.openstreetmap.org/api/0.6/map?bbox=' + str( lonlatmin[0]) + ',' + str(lonlatmin[1]) + ',' + str( lonlatmax[0]) + ',' + str(lonlatmax[1]) osmXml = urllib.urlopen(urlStr).read() osmFile.write(osmXml) #print 'Open Street Map' if os.fstat(osmFile.fileno()).st_size < 1: QMessageBox.critical(None, "Error", "No OSM data available") return osmFile.close() outputshp = self.plugin_dir + '/temp/' osmToShape = gdal_os_dep + 'ogr2ogr --config OSM_CONFIG_FILE "' + self.plugin_dir + '/osmconf.ini" -skipfailures -t_srs EPSG:' + str( rasEPSG ) + ' -overwrite -nlt POLYGON -f "ESRI Shapefile" "' + outputshp + '" "' + osmPath + '"' if sys.platform == 'win32': si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.call(osmToShape, startupinfo=si) else: os.system(osmToShape) driver = ogr.GetDriverByName('ESRI Shapefile') driver.DeleteDataSource(outputshp + 'lines.shp') driver.DeleteDataSource(outputshp + 'multilinestrings.shp') driver.DeleteDataSource(outputshp + 'other_relations.shp') driver.DeleteDataSource(outputshp + 'points.shp') osmPolygonPath = outputshp + 'multipolygons.shp' vlayer = QgsVectorLayer(osmPolygonPath, 'multipolygons', 'ogr') polygon_layer = vlayer fileInfo = QFileInfo(polygon_layer.source()) polygon_ln = fileInfo.baseName() def renameField(srcLayer, oldFieldName, newFieldName): ds = gdal.OpenEx(srcLayer.source(), gdal.OF_VECTOR | gdal.OF_UPDATE) ds.ExecuteSQL('ALTER TABLE {} RENAME COLUMN {} TO {}'.format( srcLayer.name(), oldFieldName, newFieldName)) srcLayer.reload() vlayer.startEditing() renameField(vlayer, 'building_l', 'bld_levels') renameField(vlayer, 'building_h', 'bld_hght') renameField(vlayer, 'building_c', 'bld_colour') renameField(vlayer, 'building_m', 'bld_materi') renameField(vlayer, 'building_u', 'bld_use') vlayer.commitChanges() vlayer.startEditing() vlayer.dataProvider().addAttributes( [QgsField('bld_height', QVariant.Double, 'double', 3, 2)]) vlayer.updateFields() bld_lvl = vlayer.fieldNameIndex('bld_levels') hght = vlayer.fieldNameIndex('height') bld_hght = vlayer.fieldNameIndex('bld_hght') bld_height = vlayer.fieldNameIndex('bld_height') bldLvlHght = float(self.dlg.doubleSpinBoxBldLvl.value()) illegal_chars = string.ascii_letters + "!#$%&'*+^_`|~:" + " " counterNone = 0 counter = 0 #counterWeird = 0 for feature in vlayer.getFeatures(): if feature[hght]: try: #feature[bld_height] = float(re.sub("[^0-9]", ".", str(feature[hght]))) feature[bld_height] = float( str(feature[hght]).translate(None, illegal_chars)) except: counterNone += 1 elif feature[bld_hght]: try: #feature[bld_height] = float(re.sub("[^0-9]", ".", str(feature[bld_hght]))) feature[bld_height] = float( str(feature[bld_hght]).translate( None, illegal_chars)) except: counterNone += 1 elif feature[bld_lvl]: try: #feature[bld_height] = float(re.sub("[^0-9]", "", str(feature[bld_lvl])))*bldLvlHght feature[bld_height] = float( str(feature[bld_lvl]).translate( None, illegal_chars)) * bldLvlHght except: counterNone += 1 else: counterNone += 1 vlayer.updateFeature(feature) counter += 1 vlayer.commitChanges() flname = vlayer.attributeDisplayName(bld_height) counterDiff = counter - counterNone # Zonal statistics vlayer.startEditing() zoneStat = QgsZonalStatistics(vlayer, filepath_dem, "stat_", 1, QgsZonalStatistics.Mean) zoneStat.calculateStatistics(None) vlayer.dataProvider().addAttributes( [QgsField('height_asl', QVariant.Double)]) vlayer.updateFields() e = QgsExpression('stat_mean + ' + flname) e.prepare(vlayer.pendingFields()) idx = vlayer.fieldNameIndex('height_asl') for f in vlayer.getFeatures(): f[idx] = e.evaluate(f) vlayer.updateFeature(f) vlayer.commitChanges() vlayer.startEditing() idx2 = vlayer.fieldNameIndex('stat_mean') vlayer.dataProvider().deleteAttributes([idx2]) vlayer.updateFields() vlayer.commitChanges() self.dlg.progressBar.setValue(2) # Convert polygon layer to raster # Define pixel_size and NoData value of new raster pixel_size = self.dlg.spinBox.value() # half picture size # Create the destination data source gdalrasterize = gdal_os_dep + 'gdal_rasterize -a ' + 'height_asl' + ' -te ' + str(self.xMin) + ' ' + str(self.yMin) + ' ' + str(self.xMax) + ' ' + str(self.yMax) +\ ' -tr ' + str(pixel_size) + ' ' + str(pixel_size) + ' -l "' + str(polygon_ln) + '" "' \ + str(polygon_layer.source()) + '" "' + self.plugin_dir + '/temp/clipdsm.tif"' # gdalclipdem = gdal_os_dep + 'gdalwarp -dstnodata -9999 -q -overwrite -te ' + str(self.xMin) + ' ' + str(self.yMin) + ' ' + str(self.xMax) + ' ' + str(self.yMax) +\ # ' -tr ' + str(pixel_size) + ' ' + str(pixel_size) + \ # ' -of GTiff ' + '"' + filepath_dem + '" "' + self.plugin_dir + '/temp/clipdem.tif"' # Rasterize if sys.platform == 'win32': si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.call(gdalrasterize, startupinfo=si) # subprocess.call(gdalclipdem, startupinfo=si) gdal.Warp(self.plugin_dir + '/temp/clipdem.tif', filepath_dem, xRes=pixel_size, yRes=pixel_size) else: os.system(gdalrasterize) # os.system(gdalclipdem) gdal.Warp(self.plugin_dir + '/temp/clipdem.tif', filepath_dem, xRes=pixel_size, yRes=pixel_size) # Remove gdalwarp with gdal.Translate # bigraster = gdal.Open(filepath_dem) # bbox = (self.xMin, self.yMax, self.xMax, self.yMin) # gdal.Translate(self.plugin_dir + '/data/clipdem.tif', bigraster, projWin=bbox) self.dlg.progressBar.setValue(3) # Adding DSM to DEM # Read DEM dem_raster = gdal.Open(self.plugin_dir + '/temp/clipdem.tif') dem_array = np.array(dem_raster.ReadAsArray().astype(np.float)) dsm_raster = gdal.Open(self.plugin_dir + '/temp/clipdsm.tif') dsm_array = np.array(dsm_raster.ReadAsArray().astype(np.float)) indx = dsm_array.shape for ix in range(0, int(indx[0])): for iy in range(0, int(indx[1])): if int(dsm_array[ix, iy]) == 0: dsm_array[ix, iy] = dem_array[ix, iy] if self.dlg.checkBoxPolygon.isChecked(): vlayer.startEditing() idxHght = vlayer.fieldNameIndex('height_asl') idxBld = vlayer.fieldNameIndex('building') features = vlayer.getFeatures() #for f in vlayer.getFeatures(): for f in features: geom = f.geometry() posUnitsMetre = ['metre', 'meter', 'm'] # Possible metre units posUnitsFt = [ 'US survey foot', 'ft', 'feet', 'foot', 'ftUS', 'International foot' ] # Possible foot units if self.dem_layer_unit in posUnitsMetre: sqUnit = 1 elif self.dem_layer_unit in posUnitsFt: sqUnit = 10.76 if int(geom.area()) > 50000 * sqUnit: vlayer.deleteFeature(f.id()) #if not f[idxHght]: #vlayer.deleteFeature(f.id()) #elif not f[idxBld]: #vlayer.deleteFeature(f.id()) vlayer.updateFields() vlayer.commitChanges() QgsVectorFileWriter.writeAsVectorFormat(vlayer, str(self.OSMoutputfile), "UTF-8", None, "ESRI Shapefile") else: vlayer.startEditing() idx3 = vlayer.fieldNameIndex('height_asl') vlayer.dataProvider().deleteAttributes([idx3]) vlayer.updateFields() vlayer.commitChanges() self.dlg.progressBar.setValue(4) # Save raster def saveraster( gdal_data, filename, raster ): # gdal_data = raster extent, filename = output filename, raster = numpy array (raster to be saved) rows = gdal_data.RasterYSize cols = gdal_data.RasterXSize outDs = gdal.GetDriverByName("GTiff").Create( filename, cols, rows, int(1), gdal.GDT_Float32) outBand = outDs.GetRasterBand(1) # write the data outBand.WriteArray(raster, 0, 0) # flush data to disk, set the NoData value and calculate stats outBand.FlushCache() outBand.SetNoDataValue(-9999) # georeference the image and set the projection outDs.SetGeoTransform(gdal_data.GetGeoTransform()) outDs.SetProjection(gdal_data.GetProjection()) saveraster(dsm_raster, self.DSMoutputfile, dsm_array) # Load result into canvas rlayer = self.iface.addRasterLayer(self.DSMoutputfile) # Trigger a repaint if hasattr(rlayer, "setCacheImage"): rlayer.setCacheImage(None) rlayer.triggerRepaint() self.dlg.progressBar.setValue(5) #runTime = datetime.datetime.now() - start if self.dlg.checkBoxOSM.isChecked(): QMessageBox.information( self.dlg, 'DSM Generator', 'Operation successful! ' + str(counterDiff) + ' building polygons out of ' + str(counter) + ' contained height values.') #self.iface.messageBar().pushMessage("DSM Generator. Operation successful! " + str(counterDiff) + " buildings out of " + str(counter) + " contained height values.", level=QgsMessageBar.INFO, duration=5) else: #self.iface.messageBar().pushMessage("DSM Generator. Operation successful!", level=QgsMessageBar.INFO, duration=5) QMessageBox.information(self.dlg, 'DSM Generator', 'Operation successful!') self.resetPlugin() #print "finished run: %s\n\n" % (datetime.datetime.now() - start) def resetPlugin(self): # Reset plugin self.dlg.canvasButton.setAutoExclusive(False) self.dlg.canvasButton.setChecked(False) self.dlg.layerButton.setAutoExclusive(False) self.dlg.layerButton.setChecked(False) self.dlg.checkBoxOSM.setCheckState(0) self.dlg.checkBoxPolygon.setCheckState(0) # Extent self.layerComboManagerExtent.setCurrentIndex(-1) self.dlg.lineEditNorth.setText("") self.dlg.lineEditSouth.setText("") self.dlg.lineEditWest.setText("") self.dlg.lineEditEast.setText("") # Output boxes self.dlg.OSMtextOutput.setText("") self.dlg.DSMtextOutput.setText("") # Input raster self.layerComboManagerDEM.setCurrentIndex(-1) # Input polygon self.layerComboManagerPolygon.setCurrentIndex(-1) # Progress bar self.dlg.progressBar.setValue(0) # Spin boxes self.dlg.spinBox.setValue(2) self.dlg.doubleSpinBoxBldLvl.setValue(2.5)
class IntersectLPISModule(QDialog, FORM_CLASS): def __init__(self, parent, parents=None): """Constructor.""" super(IntersectLPISModule, self).__init__(parents) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.layerComboBox = QgsMapLayerComboBox(self) self.layerComboBox.setObjectName("layerComboBox") self.verticalLayout.addWidget(self.layerComboBox) self.verticalLayout_2.addLayout(self.verticalLayout) self.parent = parent self.iface = parent.iface self.keyLineEdit.setText(QSettings().value('gissupport/api/key')) self.saveKeyButton.clicked.connect(self.saveKey) self.addWMSButton.clicked.connect(self.addWMS) self.layerComboBox.setFocus() def addWMS(self): if not QgsProject.instance().mapLayersByName('Dzialki LPIS'): url = ("contextualWMSLegend=0&" "crs=EPSG:2180&" "dpiMode=7&" "featureCount=10&" "format=image/png8&" "layers=NumeryDzialek&layers=Dzialki&" "styles=&styles=&" "url=http://mapy.geoportal.gov.pl" "/wss/service/pub/guest/G2_GO_WMS/MapServer/WMSServer") layer = QgsRasterLayer(url, 'Dzialki LPIS', 'wms') QgsProject.instance().addMapLayer(layer) else: pass def saveKey(self): QSettings().setValue('gissupport/api/key', self.keyLineEdit.text()) def createOutputLayer(self, resp): if not QgsProject.instance().mapLayersByName('Przeciecia LPIS'): vl = QgsVectorLayer("MultiPolygon?crs=EPSG:2180", "Przeciecia LPIS", "memory") pr = vl.dataProvider() vl.startEditing() pr.addAttributes([ QgsField("id", QVariant.String), QgsField("objectid", QVariant.String), QgsField("identyfika", QVariant.String), QgsField("powierzchn", QVariant.String), QgsField("teryt", QVariant.String), QgsField("numer", QVariant.String), QgsField("obreb", QVariant.String), QgsField("wojewodztw", QVariant.String), QgsField("powiat", QVariant.String), QgsField("gmina", QVariant.String), QgsField("data_od", QVariant.String), QgsField("shape_leng", QVariant.String), QgsField("shape_area", QVariant.String) ]) vl.commitChanges() QgsProject.instance().addMapLayer(vl) for wkt in resp['data']: feature = QgsFeature() feature.setGeometry(QgsGeometry.fromWkt(wkt[0])) feature.setAttributes([a for a in wkt[1:]]) vl = QgsProject.instance().mapLayersByName('Przeciecia LPIS')[0] pr = vl.dataProvider() pr.addFeatures([feature]) vl.updateExtents() self.iface.mapCanvas().setExtent( QgsCoordinateTransform( vl.crs(), self.iface.mapCanvas().mapSettings().destinationCrs(), QgsProject.instance()).transform(vl.extent())) self.iface.mapCanvas().refresh() if resp['status'] == 'limited': self.iface.messageBar().pushMessage( 'Przeciecia LPIS', u'Wynik został ograniczony do %d działek \ ze względu na ograniczenia serwera' % len(resp['data']), level=Qgis.Warning) else: self.iface.messageBar().pushMessage('Przeciecia LPIS', u'Liczba działek przeciętych \ przez warstwę: %d' % len(resp['data']), level=Qgis.Info) def findPlots(self): vl = self.layerComboBox.currentLayer() trans = QgsCoordinateTransform(vl.crs(), QgsCoordinateReferenceSystem(2180), QgsProject.instance()) if not self.selectCheckBox.isChecked(): geom = QgsGeometry().unaryUnion( [QgsGeometry(f.geometry()) for f in vl.getFeatures()]) geom.transform(trans) if geom.isEmpty(): self.iface.messageBar().pushMessage( 'Przeciecia LPIS', u'Brak obiektów na warstwie', level=Qgis.Warning) return False else: geom = QgsGeometry().unaryUnion( [QgsGeometry(f.geometry()) for f in vl.selectedFeatures()]) geom.transform(trans) if geom.isEmpty(): self.iface.messageBar().pushMessage( 'Przeciecia LPIS', u'Brak zaznaczonych obiektów', level=Qgis.Warning) return False params = {'wkt': geom.asWkt(), 'key': self.keyLineEdit.text().strip()} data = '' try: r = urllib.request.urlopen( 'http://api.gis-support.pl/intersect?key=' + params['key'], json.dumps(params).encode('utf-8')) if r.getcode() == 403: self.iface.messageBar().pushMessage( u'Przecięcia LPIS', u'Nieprawidłowy klucz GIS Support', level=Qgis.Critical) return False resp = json.loads(r.read().decode()) data = resp['data'] except: data = 'app connection problem' if not data: self.iface.messageBar().pushMessage( u'Przecięcia LPIS', u'Warstwa nie przecina żadnej działki', level=Qgis.Warning) elif data == 'db connection problem': self.iface.messageBar().pushMessage(u'Przecięcia LPIS', u'Problem połączenia z bazą', level=Qgis.Critical) elif data == 'app connection problem': self.iface.messageBar().pushMessage( u'Przecięcia LPIS', u'Problem połączenia z aplikacją', level=Qgis.Critical) else: self.createOutputLayer(resp) return True return False def accept(self): if not self.layerComboBox.currentLayer(): self.iface.messageBar().pushMessage(u'Przecięcia LPIS', u'Podaj warstwę do przecięcia', level=Qgis.Warning) elif self.findPlots(): super(IntersectLPISModule, self).accept()
class Catchments(QDockWidget, FORM_CLASS): def __init__(self, parent, parents=None): super(Catchments, self).__init__(parents) self.setupUi(self) self.parent = parent self.iface = parent.iface self.HERE_PARAMS = { self.tr('Car'): 'car', self.tr('Pedestrian'): 'pedestrian', self.tr('Truck'): 'truck' } self.OPENROUTE_PARAMS = { self.tr('Bike'): 'cycling-regular', self.tr('Car'): 'driving-car', self.tr('Pedestrian'): 'foot-walking', self.tr('Truck'): 'driving-hgv' } self.fillDialog() def tr(self, message): return QCoreApplication.translate('Catchments', message) def fillDialog(self): self.layerComboBox = QgsMapLayerComboBox(self) self.layerComboBox.setObjectName('layerComboBox') self.layerComboBox.setFilters(QgsMapLayerProxyModel.PointLayer) self.fieldsComboBox.setFilters(QgsFieldProxyModel.Int | QgsFieldProxyModel.LongLong) self.layersLayout.addWidget(self.layerComboBox) self.providersComboBox.addItems(['HERE', 'OpenRouteService']) self.modesComboBox.addItems( [self.tr('Car'), self.tr('Pedestrian'), self.tr('Truck')]) self.trafficCheckBox.setEnabled(True) self.unitsComboBox.addItems([self.tr('Minutes'), self.tr('Meters')]) self.valueSpinBox.setMinimum(1) self.valueSpinBox.setMaximum(99999) self.valueSpinBox.setValue(10) self.getCatchments.setEnabled(False) self.getKeyLabel.setText('<html><head/><body><p>\ <a href="https://developer.here.com/?create=Evaluation&keepState=true&step=account">\ <span style=" text-decoration: underline; color:#0000ff;">Get key</span></a></p></body></html>' ) self.connectFunctions() self.loadKey() def connectFunctions(self): self.providersComboBox.currentIndexChanged.connect(self.changeProvider) self.keyLineEdit.textChanged.connect(self.saveKey) self.layerComboBox.currentIndexChanged.connect(self.changeLayerEvent) self.modesComboBox.currentIndexChanged.connect( self.disableUnnecessaryParams) self.selectCheckBox.stateChanged.connect(self.updateFeaturesQuantity) self.getCatchments.clicked.connect(self.run) def disableUnnecessaryParams(self): if self.modesComboBox.currentText() == self.tr('Pedestrian'): self.trafficCheckBox.setEnabled(False) self.trafficCheckBox.setChecked(False) elif self.providersComboBox.currentText() == 'HERE': self.trafficCheckBox.setEnabled(True) def changeProvider(self): self.modesComboBox.clear() if self.providersComboBox.currentText() == 'HERE': self.getCatchments.setToolTip('') items = [self.tr('Car'), self.tr('Pedestrian'), self.tr('Truck')] self.trafficCheckBox.setEnabled(True) self.getKeyLabel.setText('<html><head/><body><p>\ <a href="https://developer.here.com/?create=Evaluation&keepState=true&step=account">\ <span style=" text-decoration: underline; color:#0000ff;">Get key</span></a></p></body></html>' ) self.keyLineEdit.setPlaceholderText( self.tr('Insert App ID:App Code or apiKey')) elif self.providersComboBox.currentText() == 'OpenRouteService': self.getCatchments.setToolTip( self. tr('Free Plan Directions (2.000 requests per day @40 requests per minute)' )) items = [ self.tr('Bike'), self.tr('Car'), self.tr('Pedestrian'), self.tr('Truck') ] self.trafficCheckBox.setEnabled(False) self.getKeyLabel.setText('<html><head/><body><p>\ <a href="https://openrouteservice.org/plans/">\ <span style=" text-decoration: underline; color:#0000ff;">Get key</span></a></p></body></html>' ) self.keyLineEdit.setPlaceholderText(self.tr('Insert API key')) self.updateFeaturesQuantity() self.modesComboBox.addItems(items) self.loadKey() def saveKey(self): value = 'gissupport/location_lab/{}'.format( self.providersComboBox.currentText()) QSettings().setValue(value, self.keyLineEdit.text()) def loadKey(self): value = 'gissupport/location_lab/{}'.format( self.providersComboBox.currentText()) self.keyLineEdit.setText(QSettings().value(value) or '') def getPoints(self, vl, features): trans = QgsCoordinateTransform(vl.crs(), QgsCoordinateReferenceSystem(4326), QgsProject().instance()) points = [] for f in features: geom = f.geometry() geom.transform(trans) if geom.isMultipart(): points.append(geom.asMultiPoint()[0]) else: points.append(geom.asPoint()) return points def requestApi(self, points): polygons = [] not_found = 0 if self.providersComboBox.currentText() == 'HERE': """ HERE options: start string lat and lng mode string car, pedestrian or truck range int range for calculations rangetype string distance, time, consumption traffic boolean takes traffic """ key = self.keyLineEdit.text().strip().split(':') with_id = False if len(key) == 1: url = 'https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json' else: url = 'https://isoline.route.cit.api.here.com/routing/7.2/calculateisoline.json' with_id = True for p in points: params = { 'source': self.providersComboBox.currentText(), 'url': url, 'key': key, 'coords': '{x},{y}'.format(x=p[1], y=p[0]), 'transport': self.HERE_PARAMS[self.modesComboBox.currentText()], 'range': self.valueSpinBox.value() if self.unitsComboBox.currentText() == self.tr('Meters') else self.valueSpinBox.value() * 60, 'units': 'distance' if self.unitsComboBox.currentText() == self.tr('Meters') else 'time', 'traffic': 'enabled' if self.trafficCheckBox.isChecked() else 'disabled' } if not with_id: link = '{url}\ ?apiKey={key[0]}\ &mode=fastest;{transport};traffic:{traffic}\ &range={range}\ &rangetype={units}'.replace(' ', '').format(**params) else: link = '{url}\ ?app_id={key[0]}\ &app_code={key[1]}\ &mode=fastest;{transport};traffic:{traffic}\ &range={range}\ &rangetype={units}'.replace(' ', '').format(**params) coords = params['coords'] if self.pointsRouteStartCheckBox.isChecked(): link += f'&start=geo!{coords}' else: link += f'&destination=geo!{coords}' try: r = urllib.request.urlopen(link) except urllib.error.HTTPError as e: if e.code == 401: return 'invalid key' elif e.code == 403: return 'forbidden' else: continue except urllib.error.URLError as e: return f'URLError: {str(e)}' params['coordinates'] = json.loads(r.read().decode( ))['response']['isoline'][0]['component'][0]['shape'] polygons.append(params) elif self.providersComboBox.currentText() == 'OpenRouteService': """ OpenRouteService options: locations array lng range array(int) seconds for time, meters for distance range_type string time/distance profile string driving-car/driving-hgv/foot-walking (query parameter) """ location_coords = [[p[0], p[1]] for p in points] data = { 'range': [self.valueSpinBox.value()] if self.unitsComboBox.currentText() == self.tr('Meters')\ else [self.valueSpinBox.value() * 60], 'range_type': 'distance' if self.unitsComboBox.currentText() == self.tr('Meters') else 'time', 'attributes': ['reachfactor'] } if self.pointsRouteStartCheckBox.isChecked(): data['location_type'] = 'destination' locations_div = [ location_coords[i:i + 5] for i in range(0, len(location_coords), 5) ] for chunk in locations_div: self._requestORS(data, chunk, polygons) if polygons and not_found: self.iface.messageBar().pushMessage( u'Catchments', u'{} {}'.format(not_found, self.tr('catchments not found')), level=Qgis.Info) return polygons def addPolygonsToMap(self, polygons): if not QgsProject.instance().mapLayersByName( 'Location Lab - catchments'): vl = QgsVectorLayer('Polygon?crs=EPSG:4326', 'Location Lab - catchments', 'memory') pr = vl.dataProvider() vl.startEditing() pr.addAttributes([ QgsField('id', QVariant.Int), QgsField(self.tr('provider'), QVariant.String), QgsField(self.tr('mode'), QVariant.String), QgsField(self.tr('value'), QVariant.Int), QgsField(self.tr('units'), QVariant.String), QgsField(self.tr('lat'), QVariant.Double), QgsField(self.tr('lon'), QVariant.Double), QgsField('params', QVariant.String) ]) vl.commitChanges() QgsProject.instance().addMapLayer(vl) vl = QgsProject.instance().mapLayersByName( 'Location Lab - catchments')[0] pr = vl.dataProvider() next_id = len(vl.allFeatureIds()) + 1 id_field = self.fieldsComboBox.currentField() if id_field: layer = self.layerComboBox.currentLayer() new_ids = [ feature.attribute(id_field) for feature in list(layer.getFeatures()) ] for index, p in enumerate(polygons): feature = QgsFeature() points = [] if p['source'] == 'HERE' or p['source'] == 'OpenRouteService': coordinates = [c.split(',') for c in p['coordinates']] for xy in coordinates: points.append(QgsPointXY(float(xy[1]), float(xy[0]))) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolygonXY([points])) lat, lon = p['coords'].split(',') for key in ['key', 'url', 'coordinates', 'start']: #unnecessary params if key in p.keys(): p.pop(key) feature.setAttributes([ next_id if not id_field else new_ids[index], self.providersComboBox.currentText(), self.modesComboBox.currentText().lower(), self.valueSpinBox.value(), self.unitsComboBox.currentText(), float(lat), float(lon), str(p) ]) pr.addFeatures([feature]) next_id += 1 vl.updateExtents() self.iface.mapCanvas().setExtent( QgsCoordinateTransform( vl.crs(), self.iface.mapCanvas().mapSettings().destinationCrs(), QgsProject().instance()).transform(vl.extent())) self.iface.mapCanvas().refresh() def changeLayerEvent(self): vl = self.layerComboBox.currentLayer() self.fieldsComboBox.setLayer(vl) if not vl: return self.updateFeaturesQuantity() vl.selectionChanged.connect(self.updateFeaturesQuantity) vl.layerModified.connect(self.updateFeaturesQuantity) def updateFeaturesQuantity(self): vl = self.layerComboBox.currentLayer() if not vl: return if self.selectCheckBox.isChecked(): features = [f for f in vl.selectedFeatures()] else: features = [f for f in vl.getFeatures()] self.pointsLabel.setText('{} {}'.format(self.tr('Number of points:'), len(features))) if len(features) == 0: self.getCatchments.setEnabled(False) else: self.getCatchments.setEnabled(True) def show(self): self.changeLayerEvent() self.iface.addDockWidget(Qt.LeftDockWidgetArea, self) super(Catchments, self).show() def run(self): vl = self.layerComboBox.currentLayer() if self.selectCheckBox.isChecked(): features = [f for f in vl.selectedFeatures()] else: features = [f for f in vl.getFeatures()] if not features: self.iface.messageBar().pushMessage(u'Catchments', self.tr(u'No geometry'), level=Qgis.Warning) return points = self.getPoints(vl, features) polygons = self.requestApi(points) if not polygons: self.iface.messageBar().pushMessage( u'Catchments', self.tr(u'Catchments not found'), level=Qgis.Warning) return elif polygons == 'invalid key': self.iface.messageBar().pushMessage(u'Catchments', self.tr(u'Invalid API key'), level=Qgis.Warning) return elif polygons == 'forbidden': self.iface.messageBar().pushMessage( u'Catchments', self.tr(u'These credentials do not authorize access'), level=Qgis.Warning) return elif polygons == 'Daily quota reached or API key unauthorized': self.iface.messageBar().pushMessage( u'Catchments', self. tr(u'Openservice API daily quota reached or API key unauthorized' ), level=Qgis.Warning) return elif "URLError" in polygons: error_message = polygons.split("URLError: ")[1].replace( ">", "").replace("<", "") error_info = self.tr( f'An error occured while executing HERE API request. Error Message: ' ) self.iface.messageBar().pushMessage(u'Catchments', error_info + error_message, level=Qgis.Warning) return self.addPolygonsToMap(polygons) @sleep_and_retry @RateLimitDecorator(calls=20, period=60) def _requestORS(self, data, locations, output): url = 'https://api.openrouteservice.org/v2/isochrones' data['locations'] = locations r = requests.post( '{}/{}'.format( url, self.OPENROUTE_PARAMS[self.modesComboBox.currentText()]), json=data, headers={'Authorization': self.keyLineEdit.text().strip()}) data.pop('locations') if r.status_code == 403: output = r.json()['error'] elif r.status_code == 200: data.update({ 'source': self.providersComboBox.currentText(), 'url': url }) features = r.json()['features'] for index, feature in enumerate(features): feature_coords = feature['geometry']['coordinates'][0] feature_data = dict(data) feature_data.update({ 'coords': f'{locations[index][0]}, {locations[index][1]}' }) feature_data['coordinates'] = [ str(c[1]) + ',' + str(c[0]) for c in feature_coords ] output.append(feature_data)
class QpalsLM(object): def __init__(self, project, layerlist, iface): self.tabs = None self.project = project self.layerlist = layerlist self.iface = iface self.pcfile = None self.multirunner = None def switchToNextTab(self): curridx = self.tabs.currentIndex() self.tabs.setCurrentIndex(curridx + 1) self.updateTabs() def updateTabs(self): curridx = self.tabs.currentIndex() # update tabs infile = self.settings['settings']['inFile'].currentText() if self.names[curridx] == "DTM": self.pcfile = "pointcloud.odm" if infile.endswith(".tif"): self.widgets['dtmGrid'].setEnabled(False) self.widgets['dtmImp'].setEnabled(True) self.modules['dtmImp'].setParam('inFile', infile) self.modules['dtmImp'].setParam('tileSize', '120') self.modules['dtmShade'].setParam('inFile', infile) self.modules['slope'].setParam('inFile', infile) self.modules['dtmGrid'].setParam('outFile', infile) self.addDtm() elif infile.endswith(".odm"): self.widgets['dtmGrid'].setEnabled(True) self.widgets['dtmImp'].setEnabled(False) self.modules['dtmGrid'].setParam('inFile', infile) self.pcfile = infile else: self.widgets['dtmImp'].setEnabled(True) self.widgets['dtmGrid'].setEnabled(True) self.modules['dtmImp'].setParam('inFile', infile) self.modules['dtmGrid'].setParam('inFile', "pointcloud.odm") tempf = self.settings['settings']['tempFolder'].currentText() if not os.path.isdir(tempf): try: os.makedirs(tempf) except: tempf = self.project.tempdir self.settings['settings']['tempFolder'].setText(tempf) self.project.tempdir = tempf self.project.workdir = tempf if self.names[curridx] == "3D-Modelling": self.modules['lm'].setParam('inFile', self.pcfile) if self.names[curridx] != "Editing" or "Editing (3D)": if self.section.ltool.rb: self.section.ltool.canvas.scene().removeItem( self.section.ltool.rb) self.iface.actionPan().trigger() if self.names[curridx] == "Export": outdir = self.settings['settings']['outFolder'].currentText() self.modules['exp'].setParam('outFile', os.path.join(outdir, '3DSTRULI.shp')) if not os.path.isdir(outdir) and outdir: os.makedirs(outdir) def switchToPrevTab(self): curridx = self.tabs.currentIndex() self.tabs.setCurrentIndex(curridx - 1) def snapToDtm(self): player = self.edit3d_pointlayerbox.currentLayer() llayer = self.edit3d_linelayerbox.currentLayer() rlayer = self.edit3d_dtmlayerbox.currentLayer() llayer.startEditing() points = list(player.getFeatures()) pointid = self.edit3d_currPointId.value() if points: point = points[pointid] pointGeom = point.geometry() if pointGeom.asMultiPoint(): pointGeom = pointGeom.asMultiPoint()[0] else: pointGeom = pointGeom.asPoint() pid, feat = closestpoint(llayer, QgsGeometry.fromPoint(pointGeom)) linegeom = feat.geometry().asWkb().data() olinegeom = ogr.CreateGeometryFromWkb(linegeom) dx = rlayer.rasterUnitsPerPixelX() dy = rlayer.rasterUnitsPerPixelY() xpos = pointGeom.x() ypos = pointGeom.y() # assume pixel = center xll = rlayer.extent().xMinimum() + 0.5 * dx yll = rlayer.extent().yMinimum() + 0.5 * dy xoffs = (pointGeom.x() - xll) % dx yoffs = (pointGeom.y() - yll) % dy dtm_val_ll = rlayer.dataProvider().identify( QgsPoint(xpos - dx / 2, ypos - dy / 2), QgsRaster.IdentifyFormatValue).results()[1] dtm_val_ur = rlayer.dataProvider().identify( QgsPoint(xpos + dx / 2, ypos + dy / 2), QgsRaster.IdentifyFormatValue).results()[1] dtm_val_lr = rlayer.dataProvider().identify( QgsPoint(xpos + dx / 2, ypos - dy / 2), QgsRaster.IdentifyFormatValue).results()[1] dtm_val_ul = rlayer.dataProvider().identify( QgsPoint(xpos - dx / 2, ypos + dy / 2), QgsRaster.IdentifyFormatValue).results()[1] a00 = dtm_val_ll a10 = dtm_val_lr - dtm_val_ll a01 = dtm_val_ul - dtm_val_ll a11 = dtm_val_ur + dtm_val_ll - (dtm_val_lr + dtm_val_ul) dtm_bilinear = a00 + a10 * xoffs + a01 * yoffs + a11 * xoffs * yoffs x, y = olinegeom.GetPoint_2D(pid) olinegeom.SetPoint(pid, x, y, dtm_bilinear) llayer.beginEditCommand("Snap point height to DTM") updatedGeom = QgsGeometry() updatedGeom.fromWkb(olinegeom.ExportToWkb()) llayer.dataProvider().changeGeometryValues( {feat.id(): updatedGeom}) llayer.endEditCommand() # refresh vertex editor self.showProblemPoint() def removeNode(self): player = self.edit3d_pointlayerbox.currentLayer() llayer = self.edit3d_linelayerbox.currentLayer() llayer.startEditing() points = list(player.getFeatures()) pointid = self.edit3d_currPointId.value() if points: point = points[pointid] pointGeom = point.geometry() pid, feat = closestpoint(llayer, pointGeom) llayer.beginEditCommand("Vertex removed") llayer.deleteVertex(feat.id(), pid) llayer.endEditCommand() def nextProblemPoint(self): pointid = self.edit3d_currPointId.value() self.edit3d_currPointId.setValue(pointid + 1) def showProblemPoint(self): player = self.edit3d_pointlayerbox.currentLayer() llayer = self.edit3d_linelayerbox.currentLayer() self.iface.setActiveLayer(llayer) mc = self.iface.mapCanvas() # get first layer #llayer.startEditing() #self.iface.actionVertexTool().trigger() # get point position points = list(player.getFeatures()) pointid = self.edit3d_currPointId.value() if points: point = points[pointid] pointGeom = point.geometry().asMultiPoint()[0] if point.geometry( ).asMultiPoint() else point.geometry().asPoint() t = QgsCoordinateTransform(llayer.crs(), mc.mapSettings().destinationCrs(), QgsCoordinateTransformContext()) tCenter = t.transform(pointGeom) rect = QgsRectangle(tCenter, tCenter) mc.setExtent(rect) #pos = QgsMapTool(self.iface.mapCanvas()).toCanvasCoordinates(tCenter) #click = QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) #mc.mousePressEvent(click) #mc.mousePressEvent(click) mc.refresh() #vertexDock = \ # [ch for ch in self.iface.mainWindow().findChildren(QDockWidget, "") if # ch.windowTitle() == u'Vertex Editor'] #if vertexDock: # vertexDock = vertexDock[0] # self.editingls.addWidget(vertexDock) mc.refresh() def nodeLayerChanged(self): if self.edit3d_pointlayerbox.currentLayer(): self.selectNodeBtn.setText("Next point") self.selectNodeBtn.setEnabled(True) cnt = self.edit3d_pointlayerbox.currentLayer().featureCount() - 1 self.edit3d_countLabel.setText(str(cnt)) self.edit3d_currPointId.setMaximum(cnt) def createWidget(self): self.scrollwidget = QtWidgets.QScrollArea() self.scrollwidget.setWidgetResizable(True) self.tabs = QtWidgets.QTabWidget() self.scrollwidget.setWidget(self.tabs) self.names = [ 'Settings', 'DTM', 'Slope', '2D-Approximation', 'Topologic correction', 'Editing', '3D-Modelling', 'Editing (3D)', 'Export' ] self.widgets = {} self.settings = {} self.modules = {} for idx, name in enumerate(self.names): self.widgets[name] = QtWidgets.QDialog() ls = QtWidgets.QFormLayout() # Tab-specific options if name == "Settings": desc = QtWidgets.QLabel( "Welcome to the qpals LineModeler GUI! \nThis tool will help you to detect and " "model breaklines based on a DTM and/or a point cloud using the opals module " "opalsLineModeler.\nThe process includes manual editing in QGIS (\"Editing\") " "as well as automatic dectection and modelling.\n\n" "To begin, please enter some basic information.") desc.setWordWrap(True) ls.addRow(desc) boxRun = QtWidgets.QGroupBox( "Run multiple steps automatically:") boxVL = QtWidgets.QVBoxLayout() boxRun.setLayout(boxVL) self.settings['settings'] = OrderedDict([ ('name', QtWidgets.QLineEdit()), ('inFile', QpalsDropTextbox.QpalsDropTextbox( layerlist=self.layerlist)), ('tempFolder', QpalsDropTextbox.QpalsDropTextbox()), ('outFolder', QpalsDropTextbox.QpalsDropTextbox()), ('chkDTM', QtWidgets.QCheckBox("DTM")), ('chkSlope', QtWidgets.QCheckBox("Slope")), ('chk2D', QtWidgets.QCheckBox("2D-Approximation")), ('chktopo2D', QtWidgets.QCheckBox("Topological correction")), ('chkEditing2d', QtWidgets.QLabel( "--- Manual editing of 2D-Approximations ---")), ('chk3Dmodel', QtWidgets.QCheckBox("3D-Modelling")), ('chkEditing3d', QtWidgets.QLabel("--- Manual editing of 3D-Lines ---")), ('chkExport', QtWidgets.QCheckBox("Export")), ]) for key, value in list(self.settings['settings'].items()): if isinstance(value, QpalsDropTextbox.QpalsDropTextbox): value.setMinimumContentsLength(20) value.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToMinimumContentsLength) if key.startswith("chk"): boxVL.addWidget(value) ls.addRow(QtWidgets.QLabel("Project name"), self.settings['settings']['name']) hbox_wrap = QtWidgets.QHBoxLayout() hbox_wrap.addWidget(self.settings['settings']['inFile'], stretch=1) ls.addRow(QtWidgets.QLabel("Input file (TIFF/LAS/ODM)"), hbox_wrap) hbox_wrap = QtWidgets.QHBoxLayout() hbox_wrap.addWidget(self.settings['settings']['tempFolder'], stretch=1) self.settings['settings']['tempFolder'].setPlaceholderText( "drop folder here (will be created if not exists)") ls.addRow(QtWidgets.QLabel("Folder for temporary files"), hbox_wrap) hbox_wrap = QtWidgets.QHBoxLayout() self.settings['settings']['outFolder'].setPlaceholderText( "drop folder here (will be created if not exists)") hbox_wrap.addWidget(self.settings['settings']['outFolder'], stretch=1) ls.addRow(QtWidgets.QLabel("Folder for output files"), hbox_wrap) ls.addRow(QtWidgets.QLabel("")) boxBtnRun = QtWidgets.QPushButton("Run selected steps now") boxBtnRun.clicked.connect(lambda: self.run_step("all")) boxBtnExp = QtWidgets.QPushButton( "Export selected steps to .bat") boxBtnExp.clicked.connect(self.createBatFile) # saveBtn = QtWidgets.QPushButton("Save to project file") # saveBtn.clicked.connect(self.save) boxVL.addWidget(boxBtnRun) boxVL.addWidget(boxBtnExp) # boxVL.addWidget(saveBtn) ls.addRow(boxRun) if name == "DTM": desc = QtWidgets.QLabel( "This first step will create a digital terrain model (DTM) from your point cloud data. " "Also, a shading of your DTM " "will be created for visualisation purposes. If the input file is not an ODM, one has to be " "created for the modelling process later on.") desc.setWordWrap(True) ls.addRow(desc) impmod, impscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsImport", "opalsImport", self.project, {'outFile': 'pointcloud.odm'}, ["inFile", "outFile"]) self.modules['dtmImp'] = impmod self.widgets['dtmImp'] = impscroll ls.addRow(impscroll) dtmmod, dtmscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsGrid", "opalsGrid", self.project, { 'interpolation': 'movingPlanes', 'gridSize': '1', 'outFile': 'DTM_1m.tif' }, [ "inFile", "outFile", "neighbours", "searchRadius", "interpolation" ]) self.modules['dtmGrid'] = dtmmod self.widgets['dtmGrid'] = dtmscroll dtmmod.afterRun = self.addDtm ls.addRow(dtmscroll) shdmod, shdscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsShade", "opalsShade", self.project, { 'inFile': 'DTM_1m.tif', 'outFile': 'DTM_1m_shd.tif' }, [ "inFile", "outFile", ]) self.modules['dtmShade'] = shdmod shdmod.afterRun = self.addShd ls.addRow(shdscroll) if name == "Slope": desc = QtWidgets.QLabel( "To automatically detect breaklines, a slope map is calculated. This map uses the neighboring 9" " pixels to estimate a plane. The gradient (steepest slope) is then taken, converted to a slope" "in degrees, and assigned to the pixel.") desc.setWordWrap(True) ls.addRow(desc) gfmod, gfscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsGridFeature", "opalsGridFeature", self.project, { 'feature': 'slpDeg', 'inFile': 'DTM_1m.tif', 'outFile': 'DTM_1m_slope.tif' }, ["inFile", "outFile", "feature"]) self.modules['slope'] = gfmod ls.addRow(gfscroll) if name == "2D-Approximation": desc = QtWidgets.QLabel( "The slope map is used to detect breaklines. For this, the algorithm by Canny (1986) is used.\n" "First, the slope map is convoluted with a gaussian kernel for smoothing, then the derivative " "is calculated. The two threshold parameters represent the upper and lower values for the " "binarization of the derivative map. Edges that have at least one pixel > upper threshold will be " "followed until they have a pixel < lower threshold.") desc.setWordWrap(True) ls.addRow(desc) edgeDmod, edgeDscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsEdgeDetect", "opalsEdgeDetect", self.project, { 'threshold': '2;4', 'sigmaSmooth': '1.8', 'inFile': 'DTM_1m_slope_slpDeg.tif', 'outFile': 'detected_edges.tif' }, ["inFile", "outFile", "threshold", "sigmaSmooth"]) self.modules['edgeDetect'] = edgeDmod ls.addRow(edgeDscroll) desc = QtWidgets.QLabel( "Since the output of opalsEdgeDetect is still a raster, we need to vectorize it:" ) desc.setWordWrap(True) ls.addRow(desc) vecmod, vecscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsVectorize", "opalsVectorize", self.project, { 'inFile': 'detected_edges.tif', 'outFile': 'detected_edges.shp' }, ["inFile", "outFile"]) self.modules['vectorize'] = vecmod ls.addRow(vecscroll) if name == "Topologic correction": desc = QtWidgets.QLabel( "Vectorized binary rasters usually need some topological cleaning. Here, this is done in three steps: \n" "1) Find the longest line and remove all lines < 10m\n" "2) Merge lines iteratively\n" "3) Clean up") desc.setWordWrap(True) ls.addRow(desc) lt1mod, lt1scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (1)", self.project, { 'method': 'longest', 'minLength': '10', 'snapRadius': '0', 'maxTol': '0.5', 'maxAngleDev': '75;15', 'avgDist': '3', 'inFile': 'detected_edges.shp', 'outFile': 'edges1.shp' }, ["inFile", "outFile", "method", "minLength", "maxTol"]) self.modules['lt1'] = lt1mod ls.addRow(lt1scroll) lt2mod, lt2scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (2)", self.project, { 'method': 'merge', 'minLength': '10', 'snapRadius': '3', 'maxTol': '0', 'maxAngleDev': '150;15', 'avgDist': '3', 'merge.minWeight': '0.75', 'merge.relWeightLead': '0', 'merge.maxIter': '10', 'merge.revertDist': '5', 'merge.revertInterval': '1', 'merge.searchGeneration': '4', 'merge.preventIntersection': '1', 'inFile': 'edges1.shp', 'outFile': 'edges2.shp' }, [ "inFile", "outFile", "method", "maxAngleDev", "snapRadius", "merge\..*" ]) lt2scroll.setFixedHeight(lt2scroll.height() - 200) self.modules['lt2'] = lt2mod ls.addRow(lt2scroll) lt3mod, lt3scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (3)", self.project, { 'method': 'longest', 'minLength': '25', 'snapRadius': '0', 'maxTol': '0', 'maxAngleDev': '90;15', 'avgDist': '3', 'inFile': 'edges2.shp', 'outFile': 'edges3.shp' }, ["inFile", "outFile", "method", "minLength", "maxTol"]) self.modules['lt3'] = lt3mod ls.addRow(lt3scroll) lt3mod.afterRun = self.add2DLines if name == "Editing": desc = QtWidgets.QLabel( "Please start editing the 2D approximations that have been loaded into qgis. Here are some tools " "that might help:") desc.setWordWrap(True) ls.addRow(desc) box1 = QtWidgets.QGroupBox("QuickLineModeller") from . import QpalsQuickLM self.quicklm = QpalsQuickLM.QpalsQuickLM( project=self.project, layerlist=self.layerlist, iface=self.iface) box1.setLayout(self.quicklm.fl) ls.addRow(box1) box2 = QtWidgets.QGroupBox("qpalsSection") from . import QpalsSection self.section = QpalsSection.QpalsSection( project=self.project, layerlist=self.layerlist, iface=self.iface) self.section.createWidget() box2.setLayout(self.section.ls) box2.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) ls.addRow(box2) if name == "3D-Modelling": desc = QtWidgets.QLabel( "The 2D approximations can now be used to model 3D breaklines in the pointcloud/the DTM." ) desc.setWordWrap(True) ls.addRow(desc) lmmod, lmscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineModeler", "opalsLineModeler", self.project, { #"filter": "Class[Ground]", "approxFile": "edges3.shp", "outFile": "modelled_lines.shp" }, [ "inFile", "approxFile", "outFile", "filter", "patchLength", "patchWidth", "overlap", "angle", "minLength", "pointCount", "sigmaApriori" ]) self.modules['lm'] = lmmod ls.addRow(lmscroll) lmmod.afterRun = self.add3DLines if name == "Editing (3D)": desc = QtWidgets.QLabel( "Before exporting the final product, there are a few tools to check the " "quality of the result. This includes a topological check as well as a search" "for points that have a big height difference to the DTM - and might be erraneous." ) desc.setWordWrap(True) ls.addRow(desc) self.startQualityCheckBtn = QtWidgets.QPushButton( "Start calculation") self.startQualityCheckBtn.clicked.connect( self.runProblemSearchAsync) self.QualityCheckbar = QtWidgets.QProgressBar() self.QualityCheckDtm = QgsMapLayerComboBox() self.QualityCheckDtm.setFilters( QgsMapLayerProxyModel.RasterLayer) self.QualityCheckThreshold = QtWidgets.QLineEdit("0.5") ls.addRow( QtWidgets.QLabel("DTM Layer to compare heights with"), self.QualityCheckDtm) ls.addRow( QtWidgets.QLabel("Set height difference threshold [m]"), self.QualityCheckThreshold) hb = QtWidgets.QHBoxLayout() hb.addWidget(self.QualityCheckbar) hb.addWidget(self.startQualityCheckBtn) ls.addRow(hb) line = QtWidgets.QFrame() line.setFrameShape(QtWidgets.QFrame.HLine) line.setFrameShadow(QtWidgets.QFrame.Sunken) ls.addRow(line) self.editingls = ls self.edit3d_linelayerbox = QgsMapLayerComboBox() self.edit3d_linelayerbox.setFilters( QgsMapLayerProxyModel.LineLayer) self.edit3d_pointlayerbox = QgsMapLayerComboBox() self.edit3d_pointlayerbox.setFilters( QgsMapLayerProxyModel.PointLayer) self.edit3d_dtmlayerbox = QgsMapLayerComboBox() self.edit3d_dtmlayerbox.setFilters( QgsMapLayerProxyModel.RasterLayer) self.edit3d_pointlayerbox.currentIndexChanged.connect( self.nodeLayerChanged) self.edit3d_currPointId = QSpinBox() self.edit3d_currPointId.setMinimum(0) self.edit3d_currPointId.valueChanged.connect( self.showProblemPoint) ls.addRow("Select Line Layer:", self.edit3d_linelayerbox) ls.addRow("Select Problem Point layer:", self.edit3d_pointlayerbox) self.selectNodeBtn = QtWidgets.QPushButton("Next point") self.selectNodeBtn.clicked.connect( lambda: self.edit3d_currPointId.setValue( self.edit3d_currPointId.value() + 1)) self.selectPrevNodeBtn = QtWidgets.QPushButton("Prev point") self.selectPrevNodeBtn.clicked.connect( lambda: self.edit3d_currPointId.setValue( self.edit3d_currPointId.value() - 1)) self.edit3d_countLabel = QtWidgets.QLabel() self.snapToDtmBtn = QtWidgets.QPushButton("Snap to:") self.snapToDtmBtn.clicked.connect(self.snapToDtm) self.remonveNodeBtn = QtWidgets.QPushButton("Remove") self.remonveNodeBtn.clicked.connect(self.removeNode) nextBox = QtWidgets.QHBoxLayout() nextBox.addWidget(QtWidgets.QLabel("Current point:")) nextBox.addWidget(self.edit3d_currPointId) nextBox.addWidget(QtWidgets.QLabel("/")) nextBox.addWidget(self.edit3d_countLabel) nextBox.addStretch() nextBox.addWidget(self.snapToDtmBtn) nextBox.addWidget(self.edit3d_dtmlayerbox) nextBox.addWidget(self.remonveNodeBtn) nextBox.addWidget(self.selectPrevNodeBtn) nextBox.addWidget(self.selectNodeBtn) ls.addRow(nextBox) self.nodeLayerChanged() if name == "Export": exp2mod, exp2scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsTranslate", "opalsTranslate", self.project, { 'oformat': 'shp', 'inFile': 'modelled_lines.shp', 'outFile': 'STRULI3D.shp', }, ["inFile", "outFile"]) self.modules['exp'] = exp2mod ls.addRow(exp2scroll) vl = QtWidgets.QVBoxLayout() vl.addLayout(ls, 1) navbar = QtWidgets.QHBoxLayout() next = QtWidgets.QPushButton("Next step >") next.clicked.connect(self.switchToNextTab) prev = QtWidgets.QPushButton("< Previous step") prev.clicked.connect(self.switchToPrevTab) runcurr = QtWidgets.QPushButton( "Run this step (all modules above)") runcurr.clicked.connect(lambda: self.run_step(None)) if idx > 0: navbar.addWidget(prev) navbar.addStretch() if name in [ "DTM", "Slope", "2D-Approximation", "Topologic correction", "3D-Modelling", "Export" ]: navbar.addWidget(runcurr) navbar.addStretch() if idx < len(self.names): navbar.addWidget(next) vl.addLayout(navbar) self.widgets[name].setLayout(vl) self.tabs.addTab(self.widgets[name], name) # set up connections self.tabs.currentChanged.connect(self.updateTabs) return self.scrollwidget def run_step(self, step_name=None): curridx = self.tabs.currentIndex() if step_name is None: step_name = self.names[curridx] modules = self.get_step_modules(step_name=step_name) self.multirunner = qMMR() for mod in modules: self.multirunner.add_module(mod, mod.updateBar, mod.run_async_finished, mod.errorBar) self.multirunner.start() def get_step_modules(self, step_name=None): steps_modules = { "DTM": [ self.modules['dtmImp'], self.modules['dtmGrid'], self.modules['dtmShade'] ], "Slope": [self.modules['slope']], "2D-Approximation": [self.modules['edgeDetect'], self.modules['vectorize']], "Topologic correction": [self.modules['lt1'], self.modules['lt2'], self.modules['lt3']], "3D-Modelling": [self.modules['lm']], "Export": [self.modules['exp']] } if step_name == "all": modules = [] if self.settings['settings']['chkDTM'].isChecked(): modules += steps_modules["DTM"] if self.settings['settings']['chkSlope'].isChecked(): modules += steps_modules["Slope"] if self.settings['settings']['chk2D'].isChecked(): modules += steps_modules["2D-Approximation"] if self.settings['settings']['chktopo2D'].isChecked(): modules += steps_modules["Topologic correction"] if self.settings['settings']['chk3Dmodel'].isChecked(): modules += steps_modules["3D-Modelling"] if self.settings['settings']['chkExport'].isChecked(): modules += steps_modules["Export"] else: modules = steps_modules[step_name] return modules def createBatFile(self): saveTo = QtWidgets.QFileDialog.getSaveFileName(None, caption='Save to file') try: f = open(saveTo, 'w') f.write("rem BATCH FILE CREATED WITH QPALS\r\n") modules = self.get_step_modules("all") for module in modules: f.write(str(module) + "\r\n") f.close() except Exception as e: raise Exception("Saving to batch failed.", e) def updateBar(self, message): out_lines = [item for item in re.split("[\n\r\b]", message) if item] percentage = out_lines[-1] # print percentage if r"%" in percentage: perc = QpalsModuleBase.get_percentage(percentage) self.secInst.progress.setValue(int(perc)) def addDtm(self): file = self.modules['dtmGrid'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addRasterLayer(file, "DTM") def addShd(self): file = self.modules['dtmShade'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addRasterLayer(file, "DTM-Shading") def add2DLines(self): file = self.modules['lt3'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addVectorLayer(file, "2D-Approximations", "ogr") def add3DLines(self): file = self.modules['lm'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addVectorLayer(file, "3D Modelled Lines", "ogr") def runProblemSearchAsync(self): linefile = self.modules['lm'].getParam('outFile').val if not os.path.isabs(linefile): linefile = os.path.join(self.project.workdir, linefile) dtm = self.QualityCheckDtm.currentLayer() dtm_thres = self.QualityCheckThreshold.text() try: dtm_thres = float(dtm_thres) except: dtm_thres = 0 self.QualityWorker = findDoubleSegments.RunWorker( linefile, os.path.join(self.project.workdir, "quality"), dtm, dtm_thres, 50, 1000) self.QualityWorker.progress.connect(self.updateQualityBar) self.QualityWorker.finished.connect(self.QualityFinished) self.QualityWorker.error.connect(self.problemSearchError) self.QualityThread = QtCore.QThread() self.QualityWorker.moveToThread(self.QualityThread) self.QualityThread.started.connect(self.QualityWorker.run) self.QualityThread.start() self.startQualityCheckBtn.setEnabled(False) self.startQualityCheckBtn.setText("processing...") def problemSearchError(self, err): (msg, e, mod) = err print(msg) def updateQualityBar(self, fl): self.QualityCheckbar.setValue(fl) def QualityFinished(self): self.startQualityCheckBtn.setEnabled(True) self.startQualityCheckBtn.setText("Start calculation") self.updateQualityBar(100) file = os.path.join(self.project.workdir, "quality", "problems.shp") self.iface.addVectorLayer(file, "Problems", "ogr") # def save(self): # import cPickle # proj = QgsProject.instance() # tabs = cPickle.dumps(self.tabs, -1) # proj.writeEntry("qpals", "linemodelerinst", tabs) # print tabs def close(self): # remove rubber band if present if self.section.ltool.rb: self.section.ltool.canvas.scene().removeItem(self.section.ltool.rb) self.iface.actionPan().trigger()
class AddConnectorsDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, iface, project): QtWidgets.QDialog.__init__(self) self.iface = iface self.setupUi(self) self.NewLinks = False self.NewNodes = False self.project = project if project is not None: self.conn = project.conn self.path_to_file = project.path_to_file self._run_layout = QGridLayout() spacer = QSpacerItem(5, 5, QSizePolicy.Expanding, QSizePolicy.Minimum) # Centroid layer frm1 = QHBoxLayout() frm1.addItem(spacer) self.CentroidLayer = QgsMapLayerComboBox() self.CentroidLayer.layerChanged.connect(self.set_fields) clabel = QLabel() clabel.setText("Centroids layer") frm1.addWidget(clabel) frm1.addWidget(self.CentroidLayer) self.CentroidLayer.setMinimumSize(450, 30) wdgt1 = QWidget() wdgt1.setLayout(frm1) self.CentroidField = QgsFieldComboBox() self.CentroidField.setMinimumSize(450, 30) frm2 = QHBoxLayout() frm2.addItem(spacer) flabel = QLabel() flabel.setText("Centroid ID field") frm2.addWidget(flabel) frm2.addWidget(self.CentroidField) wdgt2 = QWidget() wdgt2.setLayout(frm2) self.CentroidLayer.setFilters(QgsMapLayerProxyModel.PointLayer) frm3 = QHBoxLayout() self.IfMaxLength = QCheckBox() self.IfMaxLength.setChecked(True) self.IfMaxLength.setText("Connector maximum length") self.IfMaxLength.toggled.connect(self.allows_distance) frm3.addWidget(self.IfMaxLength) frm3.addItem(spacer) self.MaxLength = QLineEdit() frm3.addWidget(self.MaxLength) frm3.addItem(spacer) lblmeters = QLabel() lblmeters.setText(" meters") frm3.addWidget(lblmeters) frm3.addItem(spacer) lblnmbr = QLabel() lblnmbr.setText("Connectors per centroid") frm3.addWidget(lblnmbr) self.NumberConnectors = QComboBox() for i in range(1, 40): self.NumberConnectors.addItem(str(i)) frm3.addWidget(self.NumberConnectors) wdgt3 = QWidget() wdgt3.setLayout(frm3) layer_frame = QVBoxLayout() layer_frame.addWidget(wdgt1) layer_frame.addWidget(wdgt2) layer_frame.addWidget(wdgt3) lyrfrm = QWidget() lyrfrm.setLayout(layer_frame) # action buttons self.but_process = QPushButton() if self.project is None: self.but_process.setText("Project not loaded") self.but_process.setEnabled(False) else: self.but_process.setText("Run!") self.but_process.clicked.connect(self.run) self.but_cancel = QPushButton() self.but_cancel.setText("Cancel") self.but_cancel.clicked.connect(self.exit_procedure) self.progressbar = QProgressBar() self.progress_label = QLabel() self.progress_label.setText("...") but_frame = QHBoxLayout() but_frame.addWidget(self.progressbar, 1) but_frame.addWidget(self.progress_label, 1) but_frame.addWidget(self.but_cancel, 1) but_frame.addItem(spacer) but_frame.addWidget(self.but_process, 1) self.but_widget = QWidget() self.but_widget.setLayout(but_frame) # Progress bars and messagers self.progress_frame = QVBoxLayout() self.status_bar_files = QProgressBar() self.progress_frame.addWidget(self.status_bar_files) self.status_label_file = QLabel() self.status_label_file.setText("Extracting: ") self.progress_frame.addWidget(self.status_label_file) self.status_bar_chunks = QProgressBar() self.progress_frame.addWidget(self.status_bar_chunks) self.progress_widget = QWidget() self.progress_widget.setLayout(self.progress_frame) self.progress_widget.setVisible(False) self._run_layout.addWidget(lyrfrm) self._run_layout.addWidget(self.but_widget) self._run_layout.addWidget(self.progress_widget) list = QWidget() listLayout = QVBoxLayout() self.list_types = QTableWidget() self.list_types.setMinimumSize(180, 80) lbl = QLabel() lbl.setText("Allowed link types") listLayout.addWidget(lbl) listLayout.addWidget(self.list_types) list.setLayout(listLayout) if self.project is not None: curr = self.conn.cursor() curr.execute( "SELECT DISTINCT link_type FROM links ORDER BY link_type") ltypes = curr.fetchall() self.list_types.setRowCount(len(ltypes)) self.list_types.setColumnCount(1) for i, lt in enumerate(ltypes): self.list_types.setItem(i, 0, QTableWidgetItem(lt[0])) self.list_types.selectAll() allStuff = QWidget() allStuff.setLayout(self._run_layout) allLayout = QHBoxLayout() allLayout.addWidget(allStuff) allLayout.addWidget(list) self.setLayout(allLayout) self.resize(700, 135) # default directory self.path = standard_path() self.set_fields() self.IfMaxLength.setChecked(False) def allows_distance(self): self.MaxLength.setEnabled(False) if self.IfMaxLength.isChecked(): self.MaxLength.setEnabled(True) def run_thread(self): self.worker_thread.ProgressValue.connect( self.progress_value_from_thread) self.worker_thread.ProgressText.connect(self.progress_text_from_thread) self.worker_thread.ProgressMaxValue.connect( self.progress_range_from_thread) self.worker_thread.jobFinished.connect(self.job_finished_from_thread) self.worker_thread.start() self.show() def progress_range_from_thread(self, val): self.progressbar.setRange(0, val) def progress_value_from_thread(self, value): self.progressbar.setValue(value) def progress_text_from_thread(self, value): self.progress_label.setText(value) def set_fields(self): self.CentroidField.setLayer(self.CentroidLayer.currentLayer()) def job_finished_from_thread(self, success): self.but_process.setEnabled(True) if self.worker_thread.error is not None: qgis.utils.iface.messageBar().pushMessage( "Error during procedure: ", self.worker_thread.error, level=Qgis.Warning, duration=6) self.exit_procedure() def run(self): if self.MaxLength.isEnabled(): max_length = float(self.MaxLength.text()) else: max_length = 1000000000000 self.link_types = [] for i in range(self.list_types.rowCount()): if self.list_types.item(i, 0).isSelected(): self.link_types.append(self.list_types.item(i, 0).text()) # If we selected all, we don;t need to filter by it if len(self.link_types) == self.list_types.rowCount(): self.link_types = [] parameters = [ self.project.path_to_file, self.CentroidLayer.currentText(), self.CentroidField.currentText(), max_length, int(self.NumberConnectors.currentText()), self.link_types ] self.but_process.setEnabled(False) self.worker_thread = AddsConnectorsProcedure( qgis.utils.iface.mainWindow(), *parameters) self.run_thread() def exit_procedure(self): self.close()
class FieldMappingDialog(QDialog, FORM_CLASS): """Dialog implementation class for the InaSAFE field mapping tool.""" def __init__(self, parent=None, iface=None, setting=None): """Constructor.""" QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle(self.tr('InaSAFE Field Mapping Tool')) icon = resources_path('img', 'icons', 'show-mapping-tool.svg') self.setWindowIcon(QIcon(icon)) self.parent = parent self.iface = iface if setting is None: setting = QSettings() self.setting = setting self.keyword_io = KeywordIO() self.layer = None self.metadata = {} self.layer_input_layout = QHBoxLayout() self.layer_label = QLabel(tr('Layer')) self.layer_combo_box = QgsMapLayerComboBox() # Filter only for Polygon and Point self.layer_combo_box.setFilters( QgsMapLayerProxyModel.PolygonLayer | QgsMapLayerProxyModel.PointLayer) # Filter out a layer that don't have layer groups excepted_layers = [] for i in range(self.layer_combo_box.count()): layer = self.layer_combo_box.layer(i) try: keywords = self.keyword_io.read_keywords(layer) except (KeywordNotFoundError, NoKeywordsFoundError): excepted_layers.append(layer) continue layer_purpose = keywords.get('layer_purpose') if not layer_purpose: excepted_layers.append(layer) continue if layer_purpose == layer_purpose_exposure['key']: layer_subcategory = keywords.get('exposure') elif layer_purpose == layer_purpose_hazard['key']: layer_subcategory = keywords.get('hazard') else: layer_subcategory = None field_groups = get_field_groups(layer_purpose, layer_subcategory) if len(field_groups) == 0: excepted_layers.append(layer) continue self.layer_combo_box.setExceptedLayerList(excepted_layers) # Select the active layer. if self.iface.activeLayer(): found = self.layer_combo_box.findText( self.iface.activeLayer().name()) if found > -1: self.layer_combo_box.setLayer(self.iface.activeLayer()) self.field_mapping_widget = None self.main_stacked_widget.setCurrentIndex(1) # Input self.layer_input_layout.addWidget(self.layer_label) self.layer_input_layout.addWidget(self.layer_combo_box) self.header_label = QLabel() self.header_label.setWordWrap(True) self.main_layout.addWidget(self.header_label) self.main_layout.addLayout(self.layer_input_layout) # Signal self.layer_combo_box.layerChanged.connect(self.set_layer) if self.layer_combo_box.currentLayer(): self.set_layer(self.layer_combo_box.currentLayer()) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) # Set up things for ok button self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up things for cancel button self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.clicked.connect(self.reject) def set_layer(self, layer=None, keywords=None): """Set layer and update UI accordingly. :param layer: A QgsVectorLayer. :type layer: QgsVectorLayer :param keywords: Keywords for the layer. :type keywords: dict, None """ if self.field_mapping_widget is not None: self.field_mapping_widget.setParent(None) self.field_mapping_widget.close() self.field_mapping_widget.deleteLater() self.main_layout.removeWidget(self.field_mapping_widget) self.field_mapping_widget = None if layer: self.layer = layer else: self.layer = self.layer_combo_box.currentLayer() if not self.layer: return if keywords is not None: self.metadata = keywords else: # Always read from metadata file. try: self.metadata = self.keyword_io.read_keywords(self.layer) except ( NoKeywordsFoundError, KeywordNotFoundError, MetadataReadError) as e: raise e if 'inasafe_default_values' not in self.metadata: self.metadata['inasafe_default_values'] = {} if 'inasafe_fields' not in self.metadata: self.metadata['inasafe_fields'] = {} self.field_mapping_widget = FieldMappingWidget( parent=self, iface=self.iface) self.field_mapping_widget.set_layer(self.layer, self.metadata) self.field_mapping_widget.show() self.main_layout.addWidget(self.field_mapping_widget) # Set header label group_names = [ self.field_mapping_widget.tabText(i) for i in range( self.field_mapping_widget.count())] if len(group_names) == 0: header_text = tr( 'There is no field group for this layer. Please select ' 'another layer.') self.header_label.setText(header_text) return elif len(group_names) == 1: pretty_group_name = group_names[0] elif len(group_names) == 2: pretty_group_name = group_names[0] + tr(' and ') + group_names[1] else: pretty_group_name = ', '.join(group_names[:-1]) pretty_group_name += tr(', and {0}').format(group_names[-1]) header_text = tr( 'Please fill the information for every tab to determine the ' 'attribute for {0} group.').format(pretty_group_name) self.header_label.setText(header_text) @pyqtSlot(bool) # prevents actions being handled twice def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2.1 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded: 3.2.1 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = field_mapping_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def save_metadata(self): """Save metadata based on the field mapping state.""" metadata = self.field_mapping_widget.get_field_mapping() for key, value in list(metadata['fields'].items()): # Delete the key if it's set to None if key in self.metadata['inasafe_default_values']: self.metadata['inasafe_default_values'].pop(key) if value is None or value == []: if key in self.metadata['inasafe_fields']: self.metadata['inasafe_fields'].pop(key) else: self.metadata['inasafe_fields'][key] = value for key, value in list(metadata['values'].items()): # Delete the key if it's set to None if key in self.metadata['inasafe_fields']: self.metadata['inasafe_fields'].pop(key) if value is None: if key in self.metadata['inasafe_default_values']: self.metadata['inasafe_default_values'].pop(key) else: self.metadata['inasafe_default_values'][key] = value # Save metadata try: self.keyword_io.write_keywords( layer=self.layer, keywords=self.metadata) except InaSAFEError as e: error_message = get_error_message(e) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QMessageBox.warning( self, self.tr('InaSAFE'), ((self.tr( 'An error was encountered when saving the following ' 'keywords:\n %s') % error_message.to_html()))) # Update setting fir recent value if self.metadata.get('inasafe_default_values'): for key, value in \ list(self.metadata['inasafe_default_values'].items()): set_inasafe_default_value_qsetting( self.setting, key, RECENT, value) def accept(self): """Method invoked when OK button is clicked.""" try: self.save_metadata() except InvalidValidationException as e: display_warning_message_box( self, tr('Invalid Field Mapping'), str(e)) return super(FieldMappingDialog, self).accept()
class TinTools: def __init__(self, iface): self.iface = iface self.toolbar = None self.highlight = None self.msgBar = iface.messageBar() self.plugin_dir = os.path.dirname(__file__) print('plugin init en ' + self.plugin_dir) def initGui(self): self.toolbar = self.iface.addToolBar(u'TIN Tools') self.toolbar.setObjectName(u'tintools') label = QLabel('Capa TIN:') self.toolbar.addWidget(label) self.tin_cb = QgsMapLayerComboBox() self.tin_cb.setMinimumWidth(200) self.tin_cb.setFilters(QgsMapLayerProxyModel.VectorLayer) self.tin_cb.layerChanged.connect(self.tinLayerChanged) self.toolbar.addWidget(self.tin_cb) # icon_path = self.plugin_dir +'/icon.png' # icon = QIcon(icon_path) #'\u0394 \u2B16 \u20E4 \u2350 \u21C8 \u2963 \u2B7F \u2b85 \u23C4 \u25e9' self.calc_plane_action = QAction('\u0394', self.iface.mainWindow()) self.calc_plane_action.triggered.connect(self.calcPlaneEquation) self.toolbar.addAction(self.calc_plane_action) self.calc_z = QAction('\u21C8', self.iface.mainWindow()) self.calc_z.triggered.connect(self.calcZ) self.toolbar.addAction(self.calc_z) self.adjust_to_tin = QAction('\u25e9', self.iface.mainWindow()) self.adjust_to_tin.triggered.connect(self.adjustToTin) self.toolbar.addAction(self.adjust_to_tin) self.tinLayerChanged(self.tin_cb.currentLayer()) def unload(self): self.planeCalc = None self.setHighlight(None, None) self.iface.removeToolBarIcon(self.calc_plane_action) self.iface.removeToolBarIcon(self.calc_z) self.iface.removeToolBarIcon(self.adjust_to_tin) del self.toolbar print('unload toolbar5') def msg(self, text): self.msgBar.pushWarning('TIN tool', text) def info(self, text): self.msgBar.pushInfo('TIN tool', text) def setHighlight(self, ly, vertex): if self.highlight: self.highlight.hide() self.highlight = None if vertex: color = QColor(255, 0, 100, 255) lv = vertex + [vertex[0]] g = QgsGeometry.fromPolyline(lv).convertToType(2) print(g.asWkt(3)) self.highlight = QgsHighlight(self.iface.mapCanvas(), g, ly) self.highlight.setColor(color) self.highlight.setWidth(5) color.setAlpha(50) self.highlight.setFillColor(color) self.highlight.show() def tinLayerChanged(self, layer): self.calc_plane_action.setEnabled(layer is not None) self.adjust_to_tin.setEnabled(layer is not None) self.calc_z.setEnabled(False) self.planeCalc = None self.setHighlight(None, None) def calcPlaneEquation(self): ly = self.tin_cb.currentLayer() self.setHighlight(None, None) if not ly: self.msg('No hay ninguna capa seleccionada') return ver = None feat = ly.selectedFeatures() geometry_type = ly.geometryType() if geometry_type == 0: # point if len(feat) == 3: ver = [f.geometry().vertexAt(0) for f in feat] else: self.msg('Hay que seleccionar 3 puntos') elif geometry_type == 2: #polygon if len(feat) == 1: geom = feat[0].geometry() ver = list(geom.vertices()) if len(ver) == 4: ver = ver[:3] else: self.msg('El polígono tiene que ser un triángulo') else: self.msg('Seleccionar solo un triángulo') else: self.msg('seleccionar tres puntos o un triángulo') if ver: self.planeCalc = PlaneCalc(ver) self.info( 'Selecciona los elementos de una capa para calcular su Z en el plano' ) self.calc_z.setEnabled(True) self.setHighlight(ly, ver) else: self.planeCalc = None self.calc_z.setEnabled(False) def calcZ(self): layer = self.iface.activeLayer() if not layer: QMessageBox.warning(None, 'TIN Tools', 'Selecciona elemenos de una capa') return if not layer.isEditable(): QMessageBox.information(None, 'TIN cal', 'La capa no está en modo edición') return for f in layer.getSelectedFeatures(): geom = f.geometry() n = 0 v = geom.vertexAt(0) while (v != QgsPoint(0, 0)): z = self.planeCalc.cal_z(v.x(), v.y()) v.setZ(z) geom.moveVertex(v, n) n += 1 v = geom.vertexAt(n) layer.changeGeometry(f.id(), geom) def adjustToTin(self): print('calc tin')
class UAVPreparer: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'UAVPreparer_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&UAV Preparer') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'UAVPreparer') self.toolbar.setObjectName(u'UAVPreparer') # Declare variables self.outputfile = None # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('UAVPreparer', message) def add_action( self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ # Create the dialog (after translation) and keep reference self.dlg = UAVPreparerDialog() icon = QIcon(icon_path) 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 initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/UAVPreparer/icon.png' self.add_action( icon_path, text=self.tr(self.tr(u'Unmanned Aerial Vehicle Preparer')), callback=self.run, parent=self.iface.mainWindow()) # Access the raster layer self.layerComboManagerDSM = QgsMapLayerComboBox(self.dlg.widgetDSM) self.layerComboManagerDSM.setFilters(QgsMapLayerProxyModel.RasterLayer) self.layerComboManagerDSM.setFixedWidth(175) self.layerComboManagerDSM.setCurrentIndex(-1) # Access the vector layer and an attribute field self.layerComboManagerPoint = QgsMapLayerComboBox(self.dlg.widgetPoint) self.layerComboManagerPoint.setCurrentIndex(-1) self.layerComboManagerPoint.setFilters(QgsMapLayerProxyModel.PointLayer) self.layerComboManagerPoint.setFixedWidth(175) self.layerComboManagerPointField = QgsFieldComboBox(self.dlg.widgetField) self.layerComboManagerPointField.setFilters(QgsFieldProxyModel.Numeric) self.layerComboManagerPoint.layerChanged.connect(self.layerComboManagerPointField.setLayer) # Set up of file save dialog self.fileDialog = QFileDialog() self.dlg.selectButton.clicked.connect(self.savefile) # Set up for the Help button self.dlg.helpButton.clicked.connect(self.help) # Set up for the Run button self.dlg.runButton.clicked.connect(self.start_progress) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu( self.tr(u'&UAV Preparer'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def run(self): """Run method that performs all the real work""" # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result: # Do something useful here - delete the line containing pass and # substitute with your code. pass def savefile(self): self.outputfile = self.fileDialog.getSaveFileName(None, "Save File As:", None, "Text Files (*.txt)") self.dlg.textOutput.setText(self.outputfile) def help(self): url = "https://github.com/nilswallenberg/UAVPreparer" webbrowser.open_new_tab(url) def start_progress(self): if not self.outputfile: QMessageBox.critical(None, "Error", "Specify an output file") return # Acquiring geodata and attributes dsm_layer = self.layerComboManagerDSM.currentLayer() if dsm_layer is None: QMessageBox.critical(None, "Error", "No valid raster layer is selected") return else: provider = dsm_layer.dataProvider() filepath_dsm = str(provider.dataSourceUri()) point_layer = self.layerComboManagerPoint.currentLayer() if point_layer is None: QMessageBox.critical(None, "Error", "No valid vector point layer is selected") return else: vlayer = QgsVectorLayer(point_layer.source(), "point", "ogr") point_field = self.layerComboManagerPointField.currentField() idx = vlayer.fieldNameIndex(point_field) if idx == -1: QMessageBox.critical(None, "Error", "An attribute with unique fields must be selected") return ### main code ### # set radius rSquare = int(self.dlg.spinBox.value()) # half picture size # finding ID column idx = vlayer.fieldNameIndex(point_field) numfeat = vlayer.featureCount() result = np.zeros([numfeat, 4]) self.dlg.progressBar.setRange(0, numfeat) counter = 0 for feature in vlayer.getFeatures(): self.dlg.progressBar.setValue (counter + 1) geom = feature.geometry() pp = geom.asPoint() x = pp[0] y = pp[1] gdalclipdsm = "gdalwarp -dstnodata -9999 -q -overwrite -te " + str(x - rSquare) + " " + str(y - rSquare) + \ " " + str(x + rSquare) + " " + str(y + rSquare) + " -of GTiff " + filepath_dsm + " " + self.plugin_dir + "/clipdsm.tif" #QMessageBox.critical(None, "Bla", gdalclipdsm) # call the gdal function si = subprocess.STARTUPINFO() # used to suppress cmd window si.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.call(gdalclipdsm, startupinfo=si) #subprocess.call(gdalclipdsm) dataset = gdal.Open(self.plugin_dir + "/clipdsm.tif") dsm_array = dataset.ReadAsArray().astype(np.float) result[counter, 0] = int(feature.attributes()[idx]) result[counter, 1] = np.mean(dsm_array) result[counter, 2] = np.max(dsm_array) result[counter, 3] = np.min(dsm_array) counter += 1 numformat = "%d " + "%6.2f " * 3 headertext = "id mean max min" np.savetxt(self.outputfile, result, fmt=numformat, header=headertext, comments="", delimiter="/t") self.iface.messageBar().pushMessage("UAV Preparer. Operation successful!", level=QgsMessageBar.INFO, duration=5)
class DistancePuCaWidget(PuCaWidget): """A widget for 'distance' analysis.""" def _build_widgets(self): """Builds own widgets.""" self.lastRefPointLayer = None self.refPointMapLayerComboBox = QgsMapLayerComboBox(self) self.refPointMapLayerComboBox.setObjectName( u'refPointMapLayerComboBox') self.refPointMapLayerComboBox.setFilters( QgsMapLayerProxyModel.PointLayer) self.refPointMapLayerComboBox.activated.connect( self._set_last_ref_point_layer) QgsMapLayerRegistry.instance().layersAdded.connect( self._rollback_ref_point_layer) QgsMapLayerRegistry.instance().layersRemoved.connect( self._reset_ref_point_layer) self._set_ref_point_layer(self.lastRefPointLayer) self.vBoxLayout.addWidget(self.refPointMapLayerComboBox) def _set_ref_point_layer(self, refPointLayer, lastRefPointLayer=True): """Sets the reference point layer in the refPointMapLayerComboBox. Args: refPointLayer (QgsVectorLayer): A reference to the reference point layer. lastRefPointLayer (bool): True to set self.lastRefPointLayer, False otherwise. """ if lastRefPointLayer: self.lastRefPointLayer = refPointLayer self.refPointMapLayerComboBox.setLayer(refPointLayer) def _set_last_ref_point_layer(self): """Sets the lastRefPointLayer. Sets the lastRefPointLayer according to the current layer in the refPointMapLayerComboBox. """ refPointLayer = self.refPointMapLayerComboBox.currentLayer() if refPointLayer != self.lastRefPointLayer: self.lastRefPointLayer = refPointLayer def _reset_ref_point_layer(self): """Resets the reference point layer.""" layers = self.iface.legendInterface().layers() if self.lastRefPointLayer not in layers: self._set_ref_point_layer(None) def _rollback_ref_point_layer(self): """Rolls the reference point layer back.""" if self.lastRefPointLayer == None: self._set_ref_point_layer(self.lastRefPointLayer, False) else: self.lastRefPointLayer = \ self.refPointMapLayerComboBox.currentLayer() def execute(self, layer): """Executes the analysis. Args: layer (QgsVectorLayer): A reference to the active layer. """ try: editing = self.dW.check_editing() refPointLayer = self.refPointMapLayerComboBox.currentLayer() if refPointLayer == None: self.pW.set_text_statusbar.emit( u'Žádná vrstva referenčního bodu.', 10, True) return refPointCount = refPointLayer.featureCount() refPointLayerCrs = refPointLayer.crs().authid() layerCrs = layer.crs().authid() if refPointLayerCrs != layerCrs: self.pW.set_text_statusbar.emit( u'Aktivní vrstva a vrstva referenčního bodu nemají stejný ' u'souřadnicový systém.', 10, True) return if refPointCount != 1: self.pW.set_text_statusbar.emit( u'Vrstva referenčního bodu neobsahuje právě jeden prvek.', 10, True) return layer.removeSelection() refPointLayer.removeSelection() features = self.dW.get_addressed_features(layer) self.pW.set_text_statusbar.emit( u'Provádím analýzu - měření vzdálenosti...', 0, False) refPointFeatures = refPointLayer.getFeatures() for feature in refPointFeatures: refPoint = feature.geometry().asPoint() puDistanceColumnName = self.dW.puDistanceColumnName fieldId = layer.fieldNameIndex(puDistanceColumnName) layer.startEditing() layer.updateFields() for feature in features: geometry = feature.geometry() if geometry != None: id = feature.id() originalDistance = feature.attribute(puDistanceColumnName) centroid = geometry.centroid().asPoint() distanceDouble = sqrt(refPoint.sqrDist(centroid)) distance = int(round(distanceDouble)) if distance != originalDistance: layer.changeAttributeValue(id, fieldId, distance) layer.commitChanges() if editing: self.iface.actionToggleEditing() self.pW.set_text_statusbar.emit( u'Analýza měření vzdálenosti úspěšně dokončena.', 20, False) except self.dW.puError: QgsApplication.processEvents() except: QgsApplication.processEvents() currentCheckAnalysisName = \ self.pW.checkAnalysisComboBox.currentText() self.dW.display_error_messages( self.pW, u'Error executing "{}".'.format(currentCheckAnalysisName), u'Chyba při provádění "{}".'.format(currentCheckAnalysisName)) def _set_refPoint_layer(self): """Sets current reference point layer. Sets current reference point layer to None if the last reference point layer was None. """ if self.lastRefPointLayer == None: self.refPointMapLayerComboBox.setLayer(self.lastRefPointLayer) else: self.lastRefPointLayer = \ self.refPointMapLayerComboBox.currentLayer()
class SvgBubblDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" super(SvgBubblDialog, self).__init__(parent) path_prj = QgsProject.instance().readPath("./") self.CONF_PATH = os.path.join(path_prj, "svgbubbl.conf.json") self.MAX_SPINBOX = 10000000 # self._fields_lst=[] # self._color_lst=[] self._preset_rows = PresetRows() # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) #self.setFixedSize(self.size()) #---FIX MAIN WINDOW SIZE self.bar = QgsMessageBar() self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.lyrCombobox = QgsMapLayerComboBox() self.lyrCombobox.layerChanged.connect( self.action_lyr_update) # selected layer from combobox self.horizontalLayout.addWidget(self.lyrCombobox) self.configBox.currentIndexChanged.connect(self.action_config_set) self.configPlus.clicked.connect(self.action_config_add) self.configMinus.clicked.connect(self.action_config_remove) btnAdd = QtGui.QPushButton('Add expression', self) btnAdd.setToolTip('Tooltip <b>QPushButton</b> widget') btnAdd.clicked.connect( self.action_field_add ) # connect btn action 'clicked()' and function add_field btnRem = QtGui.QPushButton('Remove expression', self) btnRem.clicked.connect( self.action_field_pop ) # connect btn action 'clicked()' and function add_field self.horizontalLayout_2.addWidget(btnAdd, 0) self.horizontalLayout_2.addWidget(btnRem, 1) self.show_preset_window() self.finished.connect(self.config_dump) #read config presets if os.path.exists(path=self.CONF_PATH): self.config_load() self.action_config_set() QgsMessageLog.logMessage("main window loaded", tag="SvgBubble") #=================================================================== # #=================================================================== def show_preset_window(self): self.mygroupbox = QtGui.QGroupBox('this is my groupbox') self.configPreset = QtGui.QFormLayout() self.mygroupbox.setLayout(self.configPreset) self.scroll = QtGui.QScrollArea() self.scroll.setWidget(self.mygroupbox) self.scroll.setWidgetResizable(True) self.verticalLayout.addWidget(self.scroll) def clear_preset_window(self): self.mygroupbox.deleteLater() self.configPreset.deleteLater() self.scroll.deleteLater() self.mygroupbox = None self.configPreset = None self.scroll = None #=========================================================================== # load config #=========================================================================== @pyqtSlot() def action_config_set(self): if self.configBox.count() > 0: #self.bar.pushMessage("configBox", "currentIndexChanged", level=QgsMessageBar.INFO) presetrows = self.configBox.itemData(self.configBox.currentIndex()) #self.bar.pushMessage("configBox", str(presetrows), level=QgsMessageBar.INFO) #self.bar.pushMessage("configBox", str(self._preset_rows), level=QgsMessageBar.INFO) self._preset_rows = presetrows.copy() self.redraw_fields() #=========================================================================== # add config preset #=========================================================================== @pyqtSlot() def action_config_add(self): text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter config name:') if ok: #if preset exist then remove old if self.configBox.findText(text) >= 0: #self.configBox.removeItem(self.configBox.findText(text)) self.configBox.setItemData(self.configBox.findText(text), self._preset_rows.copy()) else: #store new preset self.configBox.addItem(text, self._preset_rows.copy()) #select current preset as active self.configBox.setCurrentIndex(self.configBox.findText(text)) #=========================================================================== # #=========================================================================== @pyqtSlot() def action_config_remove(self): self.configBox.removeItem(self.configBox.currentIndex()) pass #========================================================================= # #========================================================================= def config_dump(self): #self.bar.pushMessage("configBox", "config_dump", level=QgsMessageBar.INFO) texts = [(self.configBox.itemText(i), self.configBox.itemData(i).dump()) for i in range(self.configBox.count())] ou_f = open(self.CONF_PATH, mode="w") json.dump(texts, ou_f) ou_f.flush() ou_f.close() # texts = [(self.configBox.itemText(i), self.configBox.itemData(i)) for i in range(self.configBox.count())] # ou_f=open(self.CONF_PATH,mode="w") # #json.dump(texts,ou_f) # pickle.dump(texts, ou_f) # ou_f.flush() # ou_f.close() #========================================================================= # #========================================================================= def config_load(self): texts = None try: texts = json.load(open(self.CONF_PATH)) except: self.bar.pushMessage("Trigger", "Defaul preset not founded", level=QgsMessageBar.INFO) pass if texts is not None: for name, text in texts: #self.bar.pushMessage("Trigger", text, level=QgsMessageBar.INFO) preset_rows = PresetRows() preset_rows.load(text) if self.configBox.findText(name) >= 0: self.configBox.setItemData(self.configBox.findText(name), preset_rows) else: self.configBox.addItem(name, preset_rows) self.action_lyr_update() # try: # dump_texts=pickle.load(file=open(self.CONF_PATH)) # self.configBox.clear() # for name,preset_rows in dump_texts: # self.configBox.addItem(name # ,preset_rows) # except:pass #=========================================================================== # #===========g================================================================ @pyqtSlot() def action_lyr_update(self): self._preset_rows.set_preset_layer(self.lyrCombobox.currentLayer()) #=============================================================== # #=============================================================== @pyqtSlot() def action_field_add(self): #self.bar.pushMessage("Trigger", "signal received", level=QgsMessageBar.INFO) field_expression_widget = QgsFieldExpressionWidget() field_expression_widget.setLayer(self.lyrCombobox.currentLayer()) color_btn_widget = QgsColorButton() spinbox = QtGui.QSpinBox() presetrow = PresetRow(qgsfieldexpressionwidget=field_expression_widget, qgscolorbutton=color_btn_widget, qspinbox=spinbox) self._preset_rows.add_preset(presetrow) self.redraw_fields() #=================================================================== # #=================================================================== @pyqtSlot() def action_field_pop(self): self._preset_rows.pop() #=========================================================================== # @param fields: # list of exprression string # @param colors: # list of QColor #=========================================================================== def redraw_fields(self): #---neeed clear widgets on self.configPreset self.clear_preset_window() self.show_preset_window() for preset in self._preset_rows: #self.bar.pushMessage("Trigger", str(preset), level=QgsMessageBar.INFO) layout = QtGui.QHBoxLayout() layout.addWidget(preset.qgscolorbutton) layout.addWidget(preset.qgsfieldexp) layout.addWidget(preset.qspinbox) self.configPreset.addRow(layout) @property def preset_rows(self): return self._preset_rows @property def expressions(self): return self._preset_rows.qgsfieldexp_lst @property def colors(self): return self._preset_rows.qgscolorbutton_lst
class PotentialSlopeFailure(object): """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'PotentialSlopeFailure_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.dlg = PotentialSlopeFailureDialog(self.iface) self.dlg.runButton.clicked.connect(self.start_progress) self.dlg.pushButtonHelp.clicked.connect(self.help) self.dlg.pushButtonSave.clicked.connect(self.folder_path) self.fileDialog = QFileDialog() self.fileDialog.setFileMode(QFileDialog.Directory) self.fileDialog.setOption(QFileDialog.ShowDirsOnly, True) # Declare instance attributes #self.actions = [] #self.menu = self.tr(u'&Potential Slope Failure') # TODO: We are going to let the user set this up in a future iteration #self.toolbar = self.iface.addToolBar(u'PotentialSlopeFailure') #self.toolbar.setObjectName(u'PotentialSlopeFailure') # self.layerComboManagerDEM = RasterLayerCombo(self.dlg.comboBoxDem) # RasterLayerCombo(self.dlg.comboBoxDem, initLayer="") # self.layerComboManagerSOIL = RasterLayerCombo(self.dlg.comboBoxSoil) # RasterLayerCombo(self.dlg.comboBoxSoil, initLayer="") self.layerComboManagerDEM = QgsMapLayerComboBox(self.dlg.widgetDEM) self.layerComboManagerDEM.setFilters(QgsMapLayerProxyModel.RasterLayer) self.layerComboManagerDEM.setFixedWidth(175) self.layerComboManagerSOIL = QgsMapLayerComboBox(self.dlg.widgetSOIL) self.layerComboManagerSOIL.setFilters( QgsMapLayerProxyModel.RasterLayer) self.layerComboManagerSOIL.setFixedWidth(175) self.folderPath = 'None' # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('PotentialSlopeFailure', message) # def add_action( # self, # icon_path, # text, # callback, # enabled_flag=True, # add_to_menu=True, # add_to_toolbar=True, # status_tip=None, # whats_this=None, # parent=None): # """Add a toolbar icon to the toolbar. # # :param icon_path: Path to the icon for this action. Can be a resource # path (e.g. ':/plugins/foo/bar.png') or a normal file system path. # :type icon_path: str # # :param text: Text that should be shown in menu items for this action. # :type text: str # # :param callback: Function to be called when the action is triggered. # :type callback: function # # :param enabled_flag: A flag indicating if the action should be enabled # by default. Defaults to True. # :type enabled_flag: bool # # :param add_to_menu: Flag indicating whether the action should also # be added to the menu. Defaults to True. # :type add_to_menu: bool # # :param add_to_toolbar: Flag indicating whether the action should also # be added to the toolbar. Defaults to True. # :type add_to_toolbar: bool # # :param status_tip: Optional text to show in a popup when mouse pointer # hovers over the action. # :type status_tip: str # # :param parent: Parent widget for the new action. Defaults None. # :type parent: QWidget # # :param whats_this: Optional text to show in the status bar when the # mouse pointer hovers over the action. # # :returns: The action that was created. Note that the action is also # added to self.actions list. # :rtype: QAction # """ # # # Create the dialog (after translation) and keep reference # # self.dlg = PotentialSlopeFailureDialog() # # icon = QIcon(icon_path) # 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 initGui(self): # Create action that will start plugin configuration self.action = QAction( QIcon(':/plugins/PotentialSlopeFailure/slopeicon.png'), 'Calculates areas prone to slope failures in cohesive soils', self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(self.tr("&Potential Slope Failure"), self.action) #"""Create the menu entries and toolbar icons inside the QGIS GUI.""" # #icon_path = ':/plugins/PotentialSlopeFailure/icon.png' #self.add_action( # icon_path, # text=self.tr(u'Calculates areas prone to slope failures in cohesive soils'), # callback=self.run, # parent=self.iface.mainWindow()) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu("&Potential Slope Failure", self.action) self.iface.removeToolBarIcon(self.action) # """Removes the plugin menu item and icon from QGIS GUI.""" # for action in self.actions: self.iface.removePluginMenu( # self.tr(u'&Potential Slope Failure'), # action) # self.iface.removeToolBarIcon(action) # remove the toolbar # del self.toolbar def run(self): self.dlg.show() self.dlg.exec_() def folder_path(self): self.fileDialog.open() result = self.fileDialog.exec_() if result == 1: self.folderPath = self.fileDialog.selectedFiles() self.dlg.textOutput.setText(self.folderPath[0]) def start_progress(self): if self.folderPath == 'None': QMessageBox.critical(None, "Error", "Select a valid output folder") return # Load DEM demlayer = self.layerComboManagerDEM.currentLayer() if demlayer is None: QMessageBox.critical(None, "Error", "No valid DEM raster layer is selected") return provider = demlayer.dataProvider() filepath_dem = str(provider.dataSourceUri()) gdal_dem = gdal.Open(filepath_dem) dem = gdal_dem.ReadAsArray().astype(np.float) sizex = dem.shape[0] sizey = dem.shape[1] geotransform = gdal_dem.GetGeoTransform() scale = 1 / geotransform[1] # Load Soil soillayer = self.layerComboManagerSOIL.currentLayer() if soillayer is None: QMessageBox.critical(None, "Error", "No valid Soil raster selected") return # load raster gdal.AllRegister() provider = soillayer.dataProvider() filePathOld = str(provider.dataSourceUri()) dataSet = gdal.Open(filePathOld) soil = dataSet.ReadAsArray().astype(np.float) soilsizex = soil.shape[0] soilsizey = soil.shape[1] if not (soilsizex == sizex) & (soilsizey == sizey): QMessageBox.critical( None, "Error", "The grids must be of same extent and resolution") return itera = int(360 / self.dlg.spinBoxIter.value()) alt = self.dlg.spinBoxAlt.value() shtot = np.zeros((sizex, sizey)) index = 0 # Inverting dem dem = dem * (-1.) + np.max(dem) self.dlg.progressBar.setRange(0, self.dlg.spinBoxIter.value()) for i in range(0, self.dlg.spinBoxIter.value()): azi = itera * i self.dlg.progressBar.setValue(i + 1) # self.iface.messageBar().pushMessage("ShadowGenerator", str(azi)) sh = shadow.shadowingfunctionglobalradiation( dem, azi, alt, scale, self.dlg, 1) shtot = shtot + sh index += 1 zon1 = shtot / index zon1[zon1 == 1] = 2 zon1[zon1 < 1] = 1 karta1a = zon1 * soil karta1a[karta1a == 0] = 3 filename = self.folderPath[0] + '/map1a.tif' self.saveraster(gdal_dem, filename, karta1a) # load result into canvas if self.dlg.checkBoxIntoCanvas.isChecked(): rlayer = self.iface.addRasterLayer(filename) # Trigger a repaint if hasattr(rlayer, "setCacheImage"): rlayer.setCacheImage(None) rlayer.triggerRepaint() rlayer.loadNamedStyle(self.plugin_dir + '/misc/map1a.qml') if hasattr(rlayer, "setCacheImage"): rlayer.setCacheImage(None) rlayer.triggerRepaint() self.dlg.progressBar.setValue(0) QMessageBox.information( self.dlg, "Calculation done!", "Output (map1a.tif) created in: " + self.folderPath[0] + "/") def help(self): url = "https://github.com/biglimp/PotentialSlopeFailure/wiki/Potential-Slope-Failure-plugin-for-QGIS" webbrowser.open_new_tab(url) def saveraster(self, gdal_data, filename, raster): rows = gdal_data.RasterYSize cols = gdal_data.RasterXSize outDs = gdal.GetDriverByName("GTiff").Create(filename, cols, rows, int(1), GDT_Float32) outBand = outDs.GetRasterBand(1) # write the data outBand.WriteArray(raster, 0, 0) # flush data to disk, set the NoData value and calculate stats outBand.FlushCache() outBand.SetNoDataValue(-9999) # georeference the image and set the projection outDs.SetGeoTransform(gdal_data.GetGeoTransform()) outDs.SetProjection(gdal_data.GetProjection())
class EditPuWidget(PuWidget): """A widget for editing.""" set_text_statusbar = pyqtSignal(str, int, bool) def _setup_self(self): """Sets up self.""" self.categoryValue = 0 self.categoryValues = (0, 1, 2, None) self.categoryName = self.dW.puCategoryColumnName self.shortCategoryName = self.categoryName[:10] self.setCategoryValue = 0 self.setObjectName(u'editFrame') self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName(u'gridLayout') self.gridLayout.setColumnStretch(1, 1) self._build_widgets() def _build_widgets(self): """Builds own widgets.""" self.set_text_statusbar.connect(self.dW.statusBar.set_text) self.lastPerimeterLayer = None self.editToolBar = QToolBar(self) self.editToolBar.setObjectName(u'editToolBar') self._set_icon_size() self.iface.initializationCompleted.connect(self._set_icon_size) self.gridLayout.addWidget(self.editToolBar, 0, 0, 1, 3) for action in self.iface.advancedDigitizeToolBar().actions(): if action.objectName() == 'mActionUndo': self.undoAction = action if action.objectName() == 'mActionRedo': self.redoAction = action self.editToolBar.addAction(self.undoAction) self.editToolBar.addAction(self.redoAction) self.editToolBar.addSeparator() self.toggleEditingAction = self.iface.actionToggleEditing() self.toggleEditingAction.setObjectName(u'toggleEditingAction') self.editToolBar.addAction(self.toggleEditingAction) self.saveActiveLayerEditsAction = \ self.iface.actionSaveActiveLayerEdits() self.saveActiveLayerEditsAction.setObjectName( u'saveActiveLayerEditsAction') self.editToolBar.addAction(self.saveActiveLayerEditsAction) self.cancelEditsAction = self.iface.actionCancelEdits() self.cancelEditsAction.setObjectName(u'cancelEditsAction') self.editToolBar.addAction(self.cancelEditsAction) self.addFeatureAction = self.iface.actionAddFeature() self.addFeatureAction.setObjectName(u'addFeatureAction') self.editToolBar.addAction(self.addFeatureAction) self.nodeToolAction = self.iface.actionNodeTool() self.nodeToolAction.setObjectName(u'nodeToolAction') self.editToolBar.addAction(self.nodeToolAction) self.deleteSelectedAction = self.iface.actionDeleteSelected() self.deleteSelectedAction.setObjectName(u'deleteSelectedAction') self.editToolBar.addAction(self.deleteSelectedAction) self.addPartAction = self.iface.actionAddPart() self.addPartAction.setObjectName(u'addPartAction') self.editToolBar.addAction(self.addPartAction) self.splitFeaturesAction = self.iface.actionSplitFeatures() self.splitFeaturesAction.setObjectName(u'splitFeaturesAction') self.editToolBar.addAction(self.splitFeaturesAction) self.perimeterLabel = QLabel(self) self.perimeterLabel.setObjectName(u'perimeterLabel') self.perimeterLabel.setText(u'Obvod:') self.gridLayout.addWidget(self.perimeterLabel, 1, 0, 1, 1) self.perimeterMapLayerComboBox = QgsMapLayerComboBox(self) self.perimeterMapLayerComboBox.setObjectName( u'perimeterMapLayerComboBox') self.perimeterMapLayerComboBox.setFilters( QgsMapLayerProxyModel.PolygonLayer) self.perimeterMapLayerComboBox.activated.connect( self.sync_perimeter_map_layer_combo_box) QgsMapLayerRegistry.instance().layersAdded.connect( self._rollback_perimeter_layer) QgsMapLayerRegistry.instance().layersRemoved.connect( self._reset_perimeter_layer) self.set_perimeter_layer(self.lastPerimeterLayer) self.gridLayout.addWidget(self.perimeterMapLayerComboBox, 1, 1, 1, 1) self.createPerimeterPushButton = QPushButton(self) self.createPerimeterPushButton.setObjectName( u'createPerimeterPushButton') self.createPerimeterPushButton.setText(u'Vytvořit') self.createPerimeterPushButton.setToolTip( u'Vytvořit vrstvu obvodu (.shp) z aktivní vrstvy a načíst') self.createPerimeterPushButton.clicked.connect(self._create_perimeter) self.gridLayout.addWidget(self.createPerimeterPushButton, 1, 2, 1, 1) self.gridLayout.setRowStretch(2, 1) self.categoryLabel = QLabel(self) self.categoryLabel.setObjectName(u'categoryLabel') self.categoryLabel.setText(u'Kategorie parcel:') self.gridLayout.addWidget(self.categoryLabel, 3, 0, 1, 1) self.categoryComboBox = QComboBox(self) self.categoryComboBox.setObjectName(u'categoryComboBox') self.categoryComboBox.addItem(u'mimo obvod (0)') self.categoryComboBox.addItem(u'v obvodu - neřešené (1)') self.categoryComboBox.addItem(u'v obvodu - řešené (2)') self.categoryComboBox.addItem(u'bez kategorie') self.categoryComboBox.currentIndexChanged.connect( self._set_categoryValue) self.gridLayout.addWidget(self.categoryComboBox, 3, 1, 1, 1) self.selectCategoryPushButton = QPushButton(self) self.selectCategoryPushButton.setObjectName( u'selectCategoryPushButton') self.selectCategoryPushButton.setText(u'Zobrazit') self.selectCategoryPushButton.setToolTip( u'Zobrazit (vybrat) parcely v kategorii') self.selectCategoryPushButton.clicked.connect(self._select_category) self.gridLayout.addWidget(self.selectCategoryPushButton, 3, 2, 1, 1) self.gridLayout.setRowStretch(4, 1) self.setCategoryLabel = QLabel(self) self.setCategoryLabel.setObjectName(u'setCategoryLabel') self.setCategoryLabel.setText(u'Zařadit:') self.gridLayout.addWidget(self.setCategoryLabel, 5, 0, 1, 1) self.setCategoryComboBox = QComboBox(self) self.setCategoryComboBox.setObjectName(u'setCategoryComboBox') self.setCategoryComboBox.addItem(u'vybrané parcely') self.setCategoryComboBox.setItemData( 0, u'Zařadit vybrané parcely do kategorie', Qt.ToolTipRole) self.setCategoryComboBox.addItem(u'obvodem') self.setCategoryComboBox.setItemData( 1, u'Zařadit všechny parcely do kategorií na základě obvodu', Qt.ToolTipRole) self.setCategoryComboBox.currentIndexChanged.connect( self._set_setCategoryValue) self.gridLayout.addWidget(self.setCategoryComboBox, 5, 1, 1, 1) self.setCategoryPushButton = QPushButton(self) self.setCategoryPushButton.setObjectName(u'setCategoryPushButton') self.setCategoryPushButton.setText(u'Zařadit') self.setCategoryPushButton.clicked.connect( self._start_setting_pu_category) self.gridLayout.addWidget(self.setCategoryPushButton, 5, 2, 1, 1) def _set_icon_size(self): """Sets editToolBar icon size according to the current QGIS settings.""" self.editToolBar.setIconSize(self.iface.mainWindow().iconSize()) def _set_categoryValue(self): """Sets categoryValue according to the current index. categoryValue - description: 0 - mimo obvod 1 - v obvodu - neřešené 2 - v obvodu - řešené """ currentIndex = self.categoryComboBox.currentIndex() if currentIndex == 3: self.categoryValue = None else: self.categoryValue = currentIndex def _set_setCategoryValue(self): """Sets setCategoryValue according to the current index. setCategoryValue - description: 0 - vybrané parcely 1 - obvodem """ self.setCategoryValue = self.setCategoryComboBox.currentIndex() def set_perimeter_layer(self, perimeterLayer, lastPerimeterLayer=True): """Sets the perimeter layer in the perimeterMapLayerComboBox. Args: perimeterLayer (QgsVectorLayer): A reference to the perimeter layer. lastPerimeterLayer (bool): True to set self.lastPerimeterLayer, False otherwise. """ if lastPerimeterLayer: self.lastPerimeterLayer = perimeterLayer self.perimeterMapLayerComboBox.setLayer(perimeterLayer) def sync_perimeter_map_layer_combo_box(self): """Synchronizes perimeter map layer combo boxes. Synchronizes with the perimeterMapLayerComboBox in the perimeterWidget. """ perimeterLayer = self.perimeterMapLayerComboBox.currentLayer() if perimeterLayer != self.lastPerimeterLayer: self.lastPerimeterLayer = perimeterLayer self.dW.stackedWidget.checkAnalysisPuWidget\ .perimeterPuCaWidget.set_perimeter_layer(perimeterLayer) def _reset_perimeter_layer(self): """Resets the perimeter layer.""" layers = self.iface.legendInterface().layers() if self.lastPerimeterLayer not in layers: self.set_perimeter_layer(None) def _rollback_perimeter_layer(self): """Rolls the perimeter layer back.""" if self.lastPerimeterLayer == None: self.set_perimeter_layer(self.lastPerimeterLayer, False) else: self.lastPerimeterLayer = \ self.perimeterMapLayerComboBox.currentLayer() def _create_perimeter(self): """Calls methods for creating and loading perimeter layer.""" try: succes, layer = self.dW.check_layer(self) if not succes: return if layer.featureCount() == 0: self.set_text_statusbar.emit( u'Aktivní vrstva neobsahuje žádný prvek.', 10, True) return editing = self.dW.check_editing() title = u'Uložit vrstvu obvodu jako...' filters = u'.shp (*.shp)' perimeterLayerFilePath = self.dW.open_file_dialog( title, filters, False) if perimeterLayerFilePath: self.set_text_statusbar.emit(u'Vytvářím vrstvu obvodu...', 0, False) fileInfo = QFileInfo(perimeterLayerFilePath) if not fileInfo.suffix() == u'shp': perimeterLayerFilePath = \ fileInfo.absoluteFilePath() + u'.shp' fileInfo = QFileInfo(perimeterLayerFilePath) selectedFeaturesIds = layer.selectedFeaturesIds() perimeterLayerName = fileInfo.completeBaseName() loadedLayer = self.dW.check_loaded_layers( perimeterLayerFilePath) perimeterLayer = self._create_perimeter_layer( layer, perimeterLayerFilePath, self.categoryName, perimeterLayerName, loadedLayer) QgsApplication.processEvents() loadedLayer = self.dW.check_loaded_layers( perimeterLayerFilePath) if loadedLayer: self.iface.actionDraw().trigger() self.set_perimeter_layer(loadedLayer, False) self.sync_perimeter_map_layer_combo_box() else: QgsMapLayerRegistry.instance().addMapLayer(perimeterLayer) layer.selectByIds(selectedFeaturesIds) self.iface.setActiveLayer(layer) if editing: self.toggleEditingAction.trigger() self.set_text_statusbar.emit(u'Obvod byl úspešně vytvořen.', 15, False) except: self.dW.display_error_messages(self, u'Error creating perimeter.', u'Chyba při vytváření obvodu.') def _create_perimeter_layer(self, layer, perimeterLayerFilePath, categoryName, perimeterLayerName, loadedLayer): """Creates a perimeter layer from the given layer. Args: layer (QgsVectorLayer): A reference to the layer. perimeterLayerFilePath (str): A full path to the perimeter layer. categoryName (str): A name of the category field the layer is about to be dissolved by. perimeterLayerName (str): A name of the perimeter layer. loadedLayer (bool): A reference to the loaded layer. This is relevant only for Windows platform. Returns: QgsVectorLayer: A reference to the perimeter layer. """ fileInfo = QFileInfo(perimeterLayerFilePath) tempPerimeterLayerName = fileInfo.completeBaseName() + u'.temp' layer.removeSelection() tempPerimeterLayerPath = processing.runalg('qgis:dissolve', layer, False, categoryName, None)['OUTPUT'] tempPerimeterLayer = QgsVectorLayer(tempPerimeterLayerPath, tempPerimeterLayerName, 'ogr') if loadedLayer: perimeterLayerName = loadedLayer.name() QgsMapLayerRegistry.instance().removeMapLayer(loadedLayer) QgsApplication.processEvents() processing.runalg('qgis:multiparttosingleparts', tempPerimeterLayer, perimeterLayerFilePath) perimeterLayer = QgsVectorLayer(perimeterLayerFilePath, perimeterLayerName, 'ogr') QgsMapLayerRegistry.instance().addMapLayer(perimeterLayer) expression = QgsExpression("\"{}\" is null".format( self.shortCategoryName)) self.dW.delete_features_by_expression(perimeterLayer, expression) return perimeterLayer def set_perimeter_layer_table_config(self, perimeterLayer): """Sets perimeter layer table config. Args: layer (QgsVectorLayer): A reference to the perimeter layer. """ fields = perimeterLayer.pendingFields() tableConfig = perimeterLayer.attributeTableConfig() tableConfig.update(fields) columns = tableConfig.columns() for column in columns: if not column.name == self.shortCategoryName: column.hidden = True tableConfig.setColumns(columns) perimeterLayer.setAttributeTableConfig(tableConfig) def _start_setting_pu_category(self): """Starts setting PU category in a separate thread..""" succes, layer = self.dW.check_layer(self) if not succes: return self.executeThread = ExecuteThread(layer) self.executeThread.started.connect(self._run_setting_pu_category) self.executeThread.start() def _run_setting_pu_category(self, layer): """Calls methods for setting PU category. Args: layer (QgsVectorLayer): A reference to the layer. """ try: perimeterLayer = self.perimeterMapLayerComboBox.currentLayer() editing = self.dW.check_editing() if self.setCategoryValue == 0: self._set_category_for_selected(layer, perimeterLayer) if self.setCategoryValue == 1: self._set_category_by_perimeter(layer, perimeterLayer) if editing: self.toggleEditingAction.trigger() except: QgsApplication.processEvents() self.dW.display_error_messages( self, u'Error setting parcel category.', u'Chyba při zařazování do kategorie parcel.') def _set_category_for_selected(self, layer, perimeterLayer): """Sets a categoryValue to categoryName column for selected features. Also updates the perimeter layer. Args: layer (QgsVectorLayer): A reference to the layer. perimeterLayer (QgsVectorLayer): A reference to the perimeter layer. """ featureCount = layer.selectedFeatureCount() if featureCount == 0: self.set_text_statusbar.emit( u'V aktivní vrstvě nejsou vybrány žádné prvky.', 10, True) return currentCategory = self.categoryComboBox.currentText() warning = False if featureCount == 1: self.set_text_statusbar.emit( u'Zařazuji vybranou parcelu do kategorie "{}"...'.format( currentCategory), 0, warning) else: self.set_text_statusbar.emit( u'Zařazuji vybrané parcely do kategorie "{}"...'.format( currentCategory), 0, warning) selectedFeaturesIds = layer.selectedFeaturesIds() selectedFeatures = layer.selectedFeaturesIterator() self.dW.set_field_value_for_features(layer, selectedFeatures, self.categoryName, self.categoryValue) QgsApplication.processEvents() self.update_perimeter_layer(layer, perimeterLayer) layer.selectByIds(selectedFeaturesIds) if featureCount == 1: self.set_text_statusbar.emit( u'Vybraná parcela byla zařazena do kategorie "{}".'.format( currentCategory), 20, warning) else: self.set_text_statusbar.emit( u'Vybrané parcely byly zařazeny do kategorie "{}".'.format( currentCategory), 20, warning) def update_perimeter_layer(self, layer=None, perimeterLayer=None): """Updates the perimeter layer. Args: layer (QgsVectorLayer): A reference to the layer. perimeterLayer (QgsVectorLayer): A reference to the perimeter layer. """ if not layer: layer = self.iface.activeLayer() if not perimeterLayer: perimeterLayer = self.perimeterMapLayerComboBox.currentLayer() if not self.dW.check_perimeter_layer(perimeterLayer, layer): # SpatiaLite fix - start perimeterString = u'-obvod.shp' if not self.dW.fixedSqliteDriver: composedURI = QgsDataSourceURI(layer.source()) perimeterLayerFilePath = \ composedURI.database().split('.sdb')[0] + perimeterString else: perimeterLayerFilePath = \ layer.source().split('.db')[0] + perimeterString # SpatiaLite fix - end fileInfo = QFileInfo(perimeterLayerFilePath) perimeterLayerName = fileInfo.baseName() loadedLayer = self.dW.check_loaded_layers(perimeterLayerFilePath) perimeterLayer = self._create_perimeter_layer( layer, perimeterLayerFilePath, self.categoryName, perimeterLayerName, loadedLayer) QgsApplication.processEvents() loadedLayer = self.dW.check_loaded_layers(perimeterLayerFilePath) if loadedLayer: self.iface.actionDraw().trigger() self.set_perimeter_layer(loadedLayer, False) self.sync_perimeter_map_layer_combo_box() else: QgsMapLayerRegistry.instance().addMapLayer(perimeterLayer) else: perimeterLayerFilePath = perimeterLayer.source() perimeterLayerName = perimeterLayer.name() perimeterLayer = self._create_perimeter_layer( layer, perimeterLayerFilePath, self.categoryName, perimeterLayerName, perimeterLayer) self.set_perimeter_layer(perimeterLayer, False) self.sync_perimeter_map_layer_combo_box() QgsApplication.processEvents() self.iface.actionDraw().trigger() self.iface.setActiveLayer(layer) def _set_category_by_perimeter(self, layer, perimeterLayer): """Sets a categoryValue to categoryName column for all features according to current layer in perimeterMapLayerComboBox. Args: layer (QgsVectorLayer): A reference to the layer. perimeterLayer (QgsVectorLayer): A reference to the perimeter layer. """ if not self.dW.check_perimeter_layer(perimeterLayer, layer, self): return if perimeterLayer.featureCount() == 0: self.set_text_statusbar.emit( u'Vrstva obvodu neobsahuje žádný prvek.', 10, True) return warning = False self.set_text_statusbar.emit( u'Zařazuji parcely do kategorií na základě obvodu...', 0, warning) selectedFeaturesIds = layer.selectedFeaturesIds() perimeterSelectedFeaturesIds = perimeterLayer.selectedFeaturesIds() layer.removeSelection() perimeterLayer.removeSelection() for categoryValue in self.categoryValues: if categoryValue == None: perimeterLayer.selectAll() else: self.dW.select_features_by_field_value(perimeterLayer, self.shortCategoryName, categoryValue) if perimeterLayer.selectedFeatureCount() != 0: selectedFeaturesLayerFilePath = processing.runalg( 'qgis:saveselectedfeatures', perimeterLayer, None)['OUTPUT_LAYER'] selectedFeaturesLayer = QgsVectorLayer( selectedFeaturesLayerFilePath, 'selectedFeaturesLayer', 'ogr') processing.runalg('qgis:selectbylocation', layer, selectedFeaturesLayer, u'within', 0, 0) if categoryValue == None: layer.invertSelection() categoryValue = QPyNullVariant features = layer.selectedFeaturesIterator() self.dW.set_field_value_for_features(layer, features, self.categoryName, categoryValue) layer.selectByIds(selectedFeaturesIds) perimeterLayer.selectByIds(perimeterSelectedFeaturesIds) self.set_text_statusbar.emit( u'Zařazení parcel na základě obvodu úspěšně dokončeno.', 30, warning) def _select_category(self): """Selects features in the current category.""" try: succes, layer = self.dW.check_layer(self) if not succes: return if self.categoryValue == None: expression = QgsExpression("\"{}\" is null".format( self.categoryName)) self.dW.select_features_by_expression(layer, expression) else: self.dW.select_features_by_field_value(layer, self.categoryName, self.categoryValue) currentCategory = self.categoryComboBox.currentText() featureCount = layer.selectedFeatureCount() duration = 10 warning = False if featureCount == 0: self.set_text_statusbar.emit( u'V kategorii "{}" není žádná parcela.'.format( currentCategory), duration, warning) elif featureCount == 1: self.set_text_statusbar.emit( u'V kategorii "{}" je {} parcela.'.format( currentCategory, featureCount), duration, warning) elif 1 < featureCount < 5: self.set_text_statusbar.emit( u'V kategorii "{}" jsou {} parcely.'.format( currentCategory, featureCount), duration, warning) elif 5 <= featureCount: self.set_text_statusbar.emit( u'V kategorii "{}" je {} parcel.'.format( currentCategory, featureCount), duration, warning) except: self.dW.display_error_messages( self, u'Error selecting parcels in category.', u'Chyba při vybírání parcel v kategorii.') def check_perimeter_map_layer_combo_box(self): """Checks if there is a layer in perimeterMapLayerComboBox. Returns: bool: True when there is a layer in perimeterMapLayerComboBox, False otherwise. """ if self.perimeterMapLayerComboBox.currentLayer(): return True else: return False
class Viewer(ViewerBase, ViewerClass): trackingChanged = pyqtSignal(bool) setLocationTriggered = pyqtSignal() updateFeatures = pyqtSignal(bool) layerChanged = pyqtSignal(QgsMapLayer) clearLine = pyqtSignal() closed = pyqtSignal() def __init__(self, callbackobject, parent=None): """Constructor.""" super(Viewer, self).__init__(parent) self.setupUi(self) self.callbackobject = callbackobject self.frame = self.webview.page().mainFrame() self.actiongroup = QActionGroup(self) self.actiongroup.setExclusive(True) self.actiongroup.triggered.connect(self.action_triggered) self.measuredialog = MeasureDialog(self) self.toolbar = QToolBar() self.qgisTrackButton = self.toolbar.addAction("QGIS Track") self.qgisTrackButton.setIcon(QIcon(":/icons/track")) self.qgisTrackButton.setCheckable(True) self.qgisTrackButton.setChecked(True) self.qgisTrackButton.toggled.connect(self.trackingChanged.emit) self.setlocationaction = self.toolbar.addAction("Set location") self.setlocationaction.setIcon(QIcon(":/icons/location")) self.setlocationaction.triggered.connect( self.setLocationTriggered.emit) self.setlocationaction.setCheckable(True) self.viewfeatures = self.toolbar.addAction("Load QGIS Features") self.viewfeatures.setIcon(QIcon(":/icons/features")) self.viewfeatures.setCheckable(True) self.viewfeatures.setChecked(True) self.viewfeatures.toggled.connect(self.updateFeatures.emit) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer) self.measureaction = self.toolbar.addAction("measure") self.measureaction.setObjectName("Measure") self.measureaction.setIcon(QIcon(":/icons/measure")) self.measureaction.setCheckable(True) self.infoaction = self.toolbar.addAction("Info") self.infoaction.setObjectName("Info") self.infoaction.setIcon(QIcon(":/icons/info")) self.infoaction.setCheckable(True) self.selectaction = self.toolbar.addAction("Select") self.selectaction.setObjectName("Select") self.selectaction.setIcon(QIcon(":/icons/select")) self.selectaction.setCheckable(True) self.toolbar.addSeparator() self.deleteaction = self.toolbar.addAction("Delete") self.deleteaction.setIcon(QIcon(":/icons/delete")) self.deleteaction.triggered.connect(self.delete_selected) self.deleteaction.setEnabled(False) self.addaction = self.toolbar.addAction("Add") self.addaction.setObjectName("Add") self.addaction.setIcon(QIcon(":/icons/add")) self.addaction.setCheckable(True) self.moveaction = self.toolbar.addAction("Move") self.moveaction.setObjectName("Move") self.moveaction.setIcon(QIcon(":/icons/move")) self.moveaction.setCheckable(True) self.actiongroup.addAction(self.moveaction) self.actiongroup.addAction(self.addaction) self.actiongroup.addAction(self.infoaction) self.actiongroup.addAction(self.measureaction) self.actiongroup.addAction(self.selectaction) self.activelayercombo = QgsMapLayerComboBox() self.activelayercombo.layerChanged.connect(self.layer_changed) self.activelayeraction = self.toolbar.addWidget(self.activelayercombo) self.activelayercombo.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.activelayercombo.currentIndexChanged.connect(self.index_changed) self.zvaluecheck = QCheckBox() self.zvaluecheck.setChecked(True) self.zvaluecheck.setText("Copy Z value") self.zvaluecheck.setToolTip( "Copy Z value from viewer to new features in QGIS. Must have a field named Z to enable" ) self.zvalueaction = self.toolbar.addWidget(self.zvaluecheck) self.dockWidgetContents.layout().insertWidget(0, self.toolbar) self.webview.settings().setAttribute(QWebSettings.PluginsEnabled, True) self.webview.settings().setAttribute(QWebSettings.JavascriptEnabled, True) self.webview.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self.frame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.frame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.frame.javaScriptWindowObjectCleared.connect( self.addcallbackobject) self.measuredialog.modeCombo.currentIndexChanged.connect( self.action_triggered) self.measuredialog.clearButton.clicked.connect(self.clear_line) self.earthmine = EarthmineAPI(self.frame) def closeEvent(self, event): self.closed.emit() super(Viewer, self).closeEvent(event) def index_changed(self, index): if index == -1: self.set_button_states(False, False, False, False) def clear_line(self): self.clearLine.emit() self.earthmine.clearLine() @property def copyZvalue(self): layer = self.active_layer if not layer: return False if layer.type() == QgsMapLayer.VectorLayer and layer.geometryType( ) == QGis.Point: return self.zvaluecheck.isChecked() else: return False @property def geom(self): return self.measuredialog.geom @geom.setter def geom(self, value): self.measuredialog.geom = value self.measuredialog.update_geom_labels() @property def tracking(self): return self.qgisTrackButton.isChecked() @property def mode(self): return self.measuredialog.mode @property def active_layer(self): return self.activelayercombo.currentLayer() def layer_changed(self, layer): if not layer: self.set_button_states(False, False, False, False) return if layer.type() == QgsMapLayer.VectorLayer: enabledselecttools = layer.geometryType() in [ QGis.Line, QGis.Point ] enableedittools = layer.isEditable() enabledelete = layer.isEditable() and layer.selectedFeatureCount() enablemove = layer.geometryType( ) == QGis.Point and layer.isEditable() else: enabledselecttools = False enableedittools = False enabledelete = False enablemove = False self.set_button_states(enabledselecttools, enableedittools, enabledelete, enablemove) self.action_triggered() self.layerChanged.emit(layer) def selection_changed(self, layer): if layer == self.active_layer: enabledelete = layer.isEditable() and layer.selectedFeatureCount() self.deleteaction.setEnabled(enabledelete) def set_button_states(self, selecttools, edittools, deleteenabled, moveenabled): actions = [self.selectaction, self.infoaction] for action in actions: action.setEnabled(selecttools) editactions = [self.deleteaction, self.moveaction, self.addaction] for action in editactions: action.setEnabled(edittools) if edittools: self.deleteaction.setEnabled(deleteenabled) self.moveaction.setEnabled(moveenabled) for action in editactions: if action is self.actiongroup.checkedAction( ) and not action.isEnabled(): self.infoaction.toggle() break layer = self.active_layer if not layer: enablez = False else: enablez = layer.type( ) == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point self.zvalueaction.setEnabled(enablez) @property def current_action_color(self): action = self.actiongroup.checkedAction() color = int("0x00ff00", 16) if action == self.measureaction: if self.mode == "Vertical": color = int("0x0000ff", 16) return color def action_triggered(self, *args): action = self.actiongroup.checkedAction() layer = self.activelayercombo.currentLayer() self.clear_line() if not action: return if not action == self.measureaction and ( not layer or not layer.type() == QgsMapLayer.VectorLayer): return color = self.current_action_color actiondata = {} if action == self.measureaction: self.measuredialog.show() actiondata['mode'] = self.mode geomtype = None layerid = None else: self.measuredialog.hide() geomtype = QGis.vectorGeometryType(layer.geometryType()) layerid = layer.id() data = dict(action=action.objectName(), layer=layerid, geom=geomtype, actiondata=actiondata, color=color) self.earthmine.updateAction(data) def active_tool(self): action = self.actiongroup.checkedAction() if not action: return None return action.objectName() def update_current_layer(self, layer): self.activelayercombo.setLayer(layer) def addcallbackobject(self): self.frame.addToJavaScriptWindowObject("qgis", self.callbackobject) def loadviewer(self, url): self.webview.load(url) self.frame.addToJavaScriptWindowObject("qgis", self.callbackobject) def startViewer(self, settings): self.earthmine.startViewer(settings) def set_location(self, point): # # NOTE Set location takes WGS84 make sure you have transformed it first before sending self.earthmine.setLocation(point.x(), point.y()) def clear_features(self): self.earthmine.clearFeatures() def clear_layer_features(self, layerid): self.earthmine.clearLayerObjects(layerid) def remove_feature(self, layerid, featureid): """ :param features: A dict of layerid, id, lat, lng :return: """ self.earthmine.removeFeature(layerid, featureid) def load_features(self, layerdata, features): """ :param features: A dict of layerid, id, lat, lng :return: """ self.earthmine.loadFeatures(layerdata, features) def layer_loaded(self, layerid): return self.earthmine.layerLoaded(layerid) def clear_selection(self, layerid): self.earthmine.clearSelection(layerid) def set_selection(self, layerid, featureids, clearlast=True): self.earthmine.setSelection(layerid, featureids, clearlast) def edit_feature(self, layerid, featureid, nodes): self.earthmine.editFeature(layerid, featureid, nodes) def delete_selected(self): layer = self.active_layer layer.deleteSelectedFeatures()
class NewProjectDialog(Dialog): ''' dialog to select a layer and a name as inputs for creating a new project ''' def setupUi(self): ''' set up the user interface ''' self.setMinimumWidth(500) self.setWindowTitle('Neues Projekt erstellen') project_manager = ProjectManager() self.project_names = [p.name for p in project_manager.projects] layout = QVBoxLayout(self) label = QLabel('Name des Projekts') self.name_edit = QLineEdit() self.name_edit.textChanged.connect(self.validate) layout.addWidget(label) layout.addWidget(self.name_edit) self.path = os.path.join(project_manager.settings.TEMPLATE_PATH, 'projektflaechen') hlayout = QHBoxLayout(self) label = QLabel('Import der (Teil-)Flächen des Plangebiets') self.layer_combo = QgsMapLayerComboBox() self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer) self.source = None self.layer_combo.layerChanged.connect(self.set_layer) self.layer_combo.layerChanged.connect(self.validate) browse_button = QPushButton('...') browse_button.clicked.connect(self.browse_path) browse_button.setMaximumWidth(30) hlayout.addWidget(self.layer_combo) hlayout.addWidget(browse_button) layout.addWidget(label) layout.addLayout(hlayout) self.status_label = QLabel() layout.addWidget(self.status_label) spacer = QSpacerItem( 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) layout.addItem(spacer) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.ok_button = buttons.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) if len(self.layer_combo) > 0: self.set_layer(self.layer_combo.currentLayer()) self.layer_combo.setCurrentIndex(0) def set_layer(self, layer: QgsVectorLayer = None): ''' set layer as user selection Parameters ---------- layer : QgsVectorLayer the selected layer ''' if not layer: path = self.layer_combo.currentText() layer = QgsVectorLayer(path, 'testlayer_shp', 'ogr') self.source = layer self.validate() def browse_path(self): ''' open dialog for user input of path to a shapefile and add it to the layer-combo ''' path, sf = QFileDialog.getOpenFileName( self, 'Datei wählen', filter="Shapefile(*.shp)", directory=self.path) if path: self.path = os.path.split(path)[0] self.layer_combo.setAdditionalItems([str(path)]) self.layer_combo.setCurrentIndex(self.layer_combo.count()-1) self.set_layer() def show(self) -> Tuple[bool, str, QgsVectorLayer]: ''' show dialog and return selections made by user ''' confirmed = self.exec_() if confirmed: return confirmed, self.name_edit.text(), self.source return False, None, None def validate(self): ''' validate current input of name and layer, set the status label according to validation result ''' name = str(self.name_edit.text()) status_text = '' regexp = re.compile('[\\\/\:*?\"\'<>|]') error = False if name and regexp.search(name): status_text = ('Der Projektname darf keines der folgenden Zeichen ' 'enthalten: \/:*?"\'<>|') error = True elif name in self.project_names: status_text = ( f'Ein Projekt mit dem Namen {name} existiert bereits!\n' 'Projektnamen müssen einzigartig sein.') error = True if self.source: if not self.source.isValid(): status_text = 'Der Layer ist ungültig.' error = True elif not self.source.geometryType() == QgsWkbTypes.PolygonGeometry: status_text = 'Der Layer hat keine Polygongeometrie.' error = True self.status_label.setText(status_text) if not error and (name and self.source): self.ok_button.setEnabled(True) else: self.ok_button.setEnabled(False)
class BpejPuCaWidget(PuCaWidget): """A widget for 'BPEJ' analysis.""" def _build_widgets(self): """Builds own widgets.""" self.lastBpejLayer = None self.bpejMapLayerComboBox = QgsMapLayerComboBox(self) self.bpejMapLayerComboBox.setObjectName(u'bpejMapLayerComboBox') self.bpejMapLayerComboBox.setFilters( QgsMapLayerProxyModel.PolygonLayer) self.bpejMapLayerComboBox.activated.connect(self._set_last_bpej_layer) QgsMapLayerRegistry.instance().layersAdded.connect( self._rollback_bpej_layer) QgsMapLayerRegistry.instance().layersRemoved.connect( self._reset_bpej_layer) self.set_bpej_layer(self.lastBpejLayer) self.vBoxLayout.addWidget(self.bpejMapLayerComboBox) self.bpejFieldComboBox = QgsFieldComboBox(self) self.bpejFieldComboBox.setObjectName(u'bpejFieldComboBox') self.bpejFieldComboBox.setLayer( self.bpejMapLayerComboBox.currentLayer()) self.vBoxLayout.addWidget(self.bpejFieldComboBox) self.bpejMapLayerComboBox.layerChanged.connect( self.bpejFieldComboBox.setLayer) def set_bpej_layer(self, bpejLayer, lastBpejLayer=True): """Sets the BPEJ layer in the bpejMapLayerComboBox. Args: bpejLayer (QgsVectorLayer): A reference to the BPEJ layer. lastBpejLayer (bool): True to set self.lastBpejLayer, False otherwise. """ if lastBpejLayer: self.lastBpejLayer = bpejLayer self.bpejMapLayerComboBox.setLayer(bpejLayer) def _set_last_bpej_layer(self): """Sets the lastBpejLayer. Sets the lastBpejLayer according to the current layer in the bpejMapLayerComboBox. """ bpejLayer = self.bpejMapLayerComboBox.currentLayer() if bpejLayer != self.lastBpejLayer: self.lastBpejLayer = bpejLayer def _reset_bpej_layer(self): """Resets the BPEJ layer.""" layers = self.iface.legendInterface().layers() if self.lastBpejLayer not in layers: self.set_bpej_layer(None) def _rollback_bpej_layer(self): """Rolls the BPEJ layer back.""" if self.lastBpejLayer == None: self.set_bpej_layer(self.lastBpejLayer, False) else: self.lastBpejLayer = self.bpejMapLayerComboBox.currentLayer() def execute(self, layer): """Executes the analysis. Args: layer (QgsVectorLayer): A reference to the active layer. """ try: editing = self.dW.check_editing() bpejLayer = self.bpejMapLayerComboBox.currentLayer() if bpejLayer == None: self.pW.set_text_statusbar.emit(u'Žádná vrstva BPEJ.', 10, True) return if bpejLayer.featureCount() == 0: self.pW.set_text_statusbar.emit( u'Vrstva BPEJ neobsahuje žádný prvek.', 10, True) return bpejLayerCrs = bpejLayer.crs().authid() layerCrs = layer.crs().authid() if bpejLayerCrs != layerCrs: self.pW.set_text_statusbar.emit( u'Aktivní vrstva a vrstva BPEJ nemají stejný ' u'souřadnicový systém.', 10, True) return bpejField = self.bpejFieldComboBox.currentField() if bpejField == u'': self.pW.set_text_statusbar.emit( u'Není vybrán sloupec ceny.', 10, True) return self.pW.set_text_statusbar.emit( u'Provádím analýzu - oceňování podle BPEJ...', 0, False) layer.removeSelection() bpejLayer.removeSelection() editedBpejField = self._edit_bpej_field(bpejField, layer) unionFilePath = processing.runalg( 'qgis:union', layer, bpejLayer, None)['OUTPUT'] unionLayer = QgsVectorLayer(unionFilePath, 'unionLayer', 'ogr') expression = QgsExpression( "\"{}\" is null " "or " "\"{}\" is null " "or " "\"{}\" is null " "or " "\"{}\" != 2" .format( editedBpejField, self.dW.defaultMajorParNumberColumnName[:10], self.dW.puCategoryColumnName[:10], self.dW.puCategoryColumnName[:10])) self.dW.delete_features_by_expression(unionLayer, expression) if unionLayer.featureCount() != 0: multiToSingleFilePath = processing.runalg( 'qgis:multiparttosingleparts', unionLayer, None)['OUTPUT'] multiToSingleLayer = QgsVectorLayer( multiToSingleFilePath, 'multiToSingleLayer', 'ogr') bpejCodePrices = self._get_bpej_code_prices() rowidColumnName = self.dW.rowidColumnName prices, missingBpejCodes, bpejCodeAreasPrices = \ self._calculate_feature_prices( rowidColumnName, multiToSingleLayer, editedBpejField, bpejCodePrices) priceFieldName = self.dW.puPriceColumnName priceFieldId = layer.fieldNameIndex(priceFieldName) bpejCodeAreaPricesFieldName = \ self.dW.puBpejCodeAreaPricesColumnName bpejCodeAreaPricesFielId = layer.fieldNameIndex( bpejCodeAreaPricesFieldName) layer.startEditing() layer.updateFields() features = layer.getFeatures() for feature in features: rowid = feature.attribute(rowidColumnName) id = feature.id() price = prices[rowid] if price != 0: layer.changeAttributeValue(id, priceFieldId, price) bpejCodeAreaPrices = bpejCodeAreasPrices[rowid] bpejCodeAreaPricesStr = self._get_bpej_string( bpejCodeAreaPrices) layer.changeAttributeValue( id, bpejCodeAreaPricesFielId, bpejCodeAreaPricesStr) layer.commitChanges() if editing: self.iface.actionToggleEditing() if len(missingBpejCodes) != 0: fields = bpejLayer.pendingFields() for field in fields: if field.name() == bpejField: bpejFieldTypeName = field.typeName() break if bpejFieldTypeName.lower() == u'string': missingBpejCodesStr = \ '\'' + '\', \''.join(missingBpejCodes) + '\'' else: missingBpejCodesStr = ', '.join(missingBpejCodes) expression = QgsExpression( "\"{}\" in ({})".format(bpejField, missingBpejCodesStr)) self.dW.select_features_by_expression(bpejLayer, expression) featureCount = bpejLayer.selectedFeatureCount() duration = 15 if featureCount == 1: self.iface.messageBar().pushMessage( u'BPEJ kód vybraného prvku ve vrstvě BPEJ ' u'nebyl nalezen.', QgsMessageBar.WARNING, duration) elif featureCount > 1: self.iface.messageBar().pushMessage( u'BPEJ kódy vybraných prvků ve vrstvě BPEJ ' u'nebyly nalezeny.', QgsMessageBar.WARNING, duration) self.pW.set_text_statusbar.emit( u'Analýza oceňování podle BPEJ úspěšně dokončena.', 20, False) except self.dW.puError: QgsApplication.processEvents() except: QgsApplication.processEvents() currentCheckAnalysisName = \ self.pW.checkAnalysisComboBox.currentText() self.dW.display_error_messages( self.pW, u'Error executing "{}".'.format(currentCheckAnalysisName), u'Chyba při provádění "{}".'.format(currentCheckAnalysisName)) def _edit_bpej_field(self, bpejField, layer): """Edits BPEJ field name according to the layer fields. Args: bpejField (str): A name of the BPEJ field. layer (QgsVectorLayer): A reference to the active layer. Returns: str: An edited BPEJ field name """ bpejField = bpejField[:10] parFields = layer.pendingFields() for field in parFields: if bpejField.lower() == field.name().lower(): if len(bpejField) <= 8: bpejField = bpejField + '_2' break elif len(bpejField) == 9: bpejField = bpejField + '_' break elif len(bpejField) == 10: bpejField = bpejField[:8] + '_1' break return bpejField def _get_bpej_code_prices(self): """Returns BPEJ code prices. Returns: dict: A dictionary with BPEJ codes as keys (int) and prices as values (float). """ formatTimeStr = '%d.%m.%Y' bpejDir = QDir(self.pluginDir.path() + u'/data/bpej') bpejBaseName = u'SC_BPEJ' bpejZipFileName = bpejBaseName + u'.zip' bpejZipFilePath = bpejDir.filePath(bpejZipFileName) bpejCsvFileName = bpejBaseName + u'.csv' bpejCsvFilePath = bpejDir.filePath(bpejCsvFileName) upToDate = self._check_bpej_csv(bpejCsvFilePath, formatTimeStr) if not upToDate: testInternetUrl, bpejZipUrl = self._get_url() self._download_bpej_csv( testInternetUrl, bpejZipUrl, bpejZipFilePath, bpejCsvFileName) bpejCodePrices = self._read_bpej_csv(bpejCsvFilePath, formatTimeStr) return bpejCodePrices def _check_bpej_csv(self, bpejCsvFilePath, formatTimeStr): """Checks if the BPEJ CSV file is up-to-date. Args: bpejCsvFilePath (str): A full path to the BPEJ CSV file. formatTimeStr (str): A string for time formatting. Returns: bool: True when the BPEJ CSV file is up-to-date, False otherwise. """ modificationEpochTime = os.path.getmtime(bpejCsvFilePath) modificationDateTime = datetime.fromtimestamp(modificationEpochTime) todayDateTime = datetime.now() bpejTodayDateTime = todayDateTime.replace( hour=03, minute=06, second=0, microsecond=0) if modificationDateTime > bpejTodayDateTime: return True else: return False def _get_url(self): """Returns URL. Returns an URL for testing the internet connection and an URL of the BPEJ ZIP file. Returns: str: An URL for testing the internet connection. str: An URL of the BPEJ ZIP file. """ config = RawConfigParser() config.read(self.pluginDir.filePath(u'puplugin.cfg')) testInternetUrl = config.get('BPEJ', 'testinterneturl') bpejZipUrl = config.get('BPEJ', 'bpejzipurl') return testInternetUrl, bpejZipUrl def _download_bpej_csv( self, testInternetUrl, bpejZipUrl, bpejZipFilePath, bpejCsvFileName): """Downloads BPEJ CSV file and unzips it. Args: testInternetUrl (str): An URL for testing the internet connection. bpejZipUrl (str): An URL of the BPEJ ZIP file. bpejZipFilePath (str): A full path to the BPEJ ZIP file. bpejCsvFileName (str): A name of the BPEJ CSV file. Raises: dw.puError: When a connection to the CUZK website failed. """ try: testInternetConnection = urllib.urlopen(testInternetUrl) except: return else: testInternetConnection.close() try: testBpejConnection = urllib.urlopen(bpejZipUrl) except: raise self.dW.puError( self.dW, self.pW, u'A Connection to "{}" failed.'.format(bpejZipUrl), u'Nepodařilo se připojit k "{}"'.format(bpejZipUrl)) else: testBpejConnection.close() urllib.urlretrieve(bpejZipUrl, bpejZipFilePath) self._unzip_bpej_zip(bpejZipFilePath, bpejCsvFileName) os.remove(bpejZipFilePath) def _unzip_bpej_zip(self, bpejZipFilePath, bpejCsvFileName): """Unzips BPEJ ZIP file into the same directory. Args: bpejZipFilePath (str): A full path to the BPEJ ZIP file. bpejCsvFileName (str): A name of the BPEJ CSV file. """ fileInfo = QFileInfo(bpejZipFilePath) bpejDir = fileInfo.absolutePath() bpejZip = zipfile.ZipFile(bpejZipFilePath, 'r') bpejZipContent = bpejZip.namelist() if len(bpejZipContent) != 1: bpejZip.close() raise self.dW.puError( self.dW, self.pW, u'The structure of the BPEJ ZIP file has changed. ' u'The BPEJ ZIP file contains more than one file.', u'Struktura stahovaného BPEJ ZIP souboru se změnila.') bpejZipFirstMember = bpejZipContent[0] bpejZip.extract(bpejZipFirstMember, bpejDir) bpejZip.close() if bpejZipFirstMember != bpejCsvFileName: bpejDir = QDir(bpejDir) bpejZipFirstMemberFilePath = bpejDir.filePath(bpejZipFirstMember) bpejCsvFilePath = bpejDir.filePath(bpejCsvFileName) os.rename(bpejZipFirstMemberFilePath, bpejCsvFilePath) def _read_bpej_csv(self, bpejCsvFilePath, formatTimeStr): """Reads the BPEJ CSV file. Args: bpejCsvFilePath (str): A full path to the BPEJ CSV file. formatTimeStr (str): A string for time formatting. Returns: dict: A dictionary with BPEJ codes as keys (int) and prices as values (float). """ with open(bpejCsvFilePath, 'rb') as bpejCsvFile: bpejCsvReader = csv.reader(bpejCsvFile, delimiter=';') columnNames = bpejCsvReader.next() codeColumnIndex = columnNames.index('KOD') priceColumnIndex = columnNames.index('CENA') validFromColumnIndex = columnNames.index('PLATNOST_OD') validToColumnIndex = columnNames.index('PLATNOST_DO') todayDate = datetime.now().date() bpejCodePrices = {} for row in bpejCsvReader: if len(row) == 0: break validFromDateStr = row[validFromColumnIndex] validFromDate = datetime.strptime( validFromDateStr, formatTimeStr).date() validToDateStr = row[validToColumnIndex] if validToDateStr == '': validToDate = todayDate else: validToDate = datetime.strptime( validToDateStr, formatTimeStr).date() if validFromDate <= todayDate <= validToDate: code = int(row[codeColumnIndex]) price = row[priceColumnIndex] bpejCodePrices[code] = float(price) return bpejCodePrices def _calculate_feature_prices( self, rowidColumnName, multiToSingleLayer, bpejField, bpejCodePrices): """Calculates feature prices. Args: rowidColumnName (str): A name of rowid column. multiToSingleLayer (QgsVectorLayer): A reference to the single features layer. bpejField (str): A name of the BPEJ field. bpejCodePrices (dict): A dictionary with BPEJ codes as keys (int) and prices as values (float). Returns: defaultdict: A defaultdict with rowids as keys (long) and prices as values (float). set: A set of BPEJ codes that are not in BPEJ SCV file. defaultdict: A defaultdict with rowids as keys (long) and defaultdicts as values. defaultdict: A defaultdict with BPEJ codes (without dots) as keys (str) and defaultdicts as values. defaultdict: A defaultdict with area and prices as keys (str) and their values as values (float). """ prices = defaultdict(float) bpejCodeAreasPrices = defaultdict( lambda : defaultdict(lambda : defaultdict(float))) missingBpejCodes = set() features = multiToSingleLayer.getFeatures() for feature in features: rowid = feature.attribute(rowidColumnName) bpejCode = str(feature.attribute(bpejField)) geometry = feature.geometry() editedBpejCode = int(bpejCode.replace('.', '')) if editedBpejCode in bpejCodePrices: bpejPrice = bpejCodePrices[editedBpejCode] else: bpejPrice = 0.0 missingBpejCodes.add(bpejCode) if geometry != None: area = geometry.area() price = bpejPrice*area bpejCodeAreasPrices[rowid][editedBpejCode]['bpejPrice'] += \ bpejPrice bpejCodeAreasPrices[rowid][editedBpejCode]['area'] += area for rowid, bpejCode in bpejCodeAreasPrices.items(): for editedBpejCode, values in bpejCode.items(): values['roundedArea'] = round(values['area']) values['price'] = values['roundedArea']*values['bpejPrice'] prices[rowid] += values['price'] return (prices, missingBpejCodes, bpejCodeAreasPrices) def _get_bpej_string(self, bpejCodeAreaPrices): """Returns a BPEJ string. Args: bpejCodeAreaPrices (defaultdict): A defaultdict with BPEJ codes (without dots) as keys (str) and defaultdicts as values. defaultdict: A defaultdict with area and prices as keys (str) and their values as values (float). Returns: str: A string that contains information about BPEJ. The string consists of strings '<BPEJ code>-<BPEJ price>-<rounded Area>-<price>' for each BPEJ code in the input defaultdict separated by ', '. """ bpejCodeAreaPricesStr = '' for bpejCode, values in bpejCodeAreaPrices.items(): bpejCodeAreaPricesStr += str(bpejCode) bpejCodeAreaPricesStr += '-' bpejCodeAreaPricesStr += str(values['bpejPrice']) bpejCodeAreaPricesStr += '-' bpejCodeAreaPricesStr += str(int(values['roundedArea'])) bpejCodeAreaPricesStr += '-' bpejCodeAreaPricesStr += str(values['price']) bpejCodeAreaPricesStr += ', ' bpejCodeAreaPricesStr = \ bpejCodeAreaPricesStr.strip(', ') return bpejCodeAreaPricesStr
class QpalsQuickLM(object): def __init__(self, project, layerlist, iface): self.ui = None self.project = project self.layerlist = layerlist self.iface = iface self.createUi() def createUi(self): self.selectedChkBox = QtWidgets.QCheckBox("Use selected lines only") self.selectedChkBox.setCheckState(2) self.cmbLineLayer = QgsMapLayerComboBox() self.cmbLineLayer.setFilters(QgsMapLayerProxyModel.LineLayer) self.cmbOdmPath = QpalsDropTextbox.QpalsDropTextbox( layerlist=self.layerlist, filterrex=".*\.odm$") self.runBtn = QtWidgets.QPushButton("Run") self.runBtn.clicked.connect(self.runLM) self.ui = QtWidgets.QWidget() self.fl = QtWidgets.QFormLayout() self.ui.setLayout(self.fl) self.fl.addRow(QtWidgets.QLabel("Line layer:"), self.cmbLineLayer) self.fl.addRow(QtWidgets.QLabel("Point cloud:"), self.cmbOdmPath) self.fl.addRow(self.selectedChkBox) self.fl.addRow(self.runBtn) def runLM(self): params = {} layer = self.cmbLineLayer.currentLayer() if self.selectedChkBox.checkState() == 2: infile = tempfile.NamedTemporaryFile(delete=False) params["approxFile"] = infile.name + ".shp" infile.close() QgsVectorFileWriter.writeAsVectorFormat(layer, params["approxFile"], "utf-8", layer.crs(), "ESRI Shapefile", 1) # 1 for selected only try: os.remove(infile.name + ".prj") except: pass else: params["approxFile"] = layer.source() outfile = tempfile.NamedTemporaryFile(delete=False) params["outFile"] = outfile.name + ".shp" outfile.close() params["inFile"] = self.cmbOdmPath.text() Module = QpalsModuleBase.QpalsModuleBase(execName=os.path.join( self.project.opalspath, "opalsLineModeler.exe"), QpalsProject=self.project) paramlist = [] for param in params.keys(): paramlist.append( QpalsParameter.QpalsParameter(param, params[param], None, None, None, None, None)) Module.params = paramlist #print "running module. writing outfile to %s" % params["outFile"] moduleOut = Module.run(show=0) #print moduleOut self.iface.addVectorLayer(params["outFile"], os.path.basename("Modelled Lines"), "ogr")
class Viewer(ViewerBase, ViewerClass): trackingChanged = pyqtSignal(bool) setLocationTriggered = pyqtSignal() updateFeatures = pyqtSignal(bool) layerChanged = pyqtSignal(QgsMapLayer) clearLine = pyqtSignal() closed = pyqtSignal() def __init__(self, callbackobject, parent=None): """Constructor.""" super(Viewer, self).__init__(parent) self.setupUi(self) self.callbackobject = callbackobject self.frame = self.webview.page().mainFrame() self.actiongroup = QActionGroup(self) self.actiongroup.setExclusive(True) self.actiongroup.triggered.connect(self.action_triggered) self.measuredialog = MeasureDialog(self) self.toolbar = QToolBar() self.qgisTrackButton = self.toolbar.addAction("QGIS Track") self.qgisTrackButton.setIcon(QIcon(":/icons/track")) self.qgisTrackButton.setCheckable(True) self.qgisTrackButton.setChecked(True) self.qgisTrackButton.toggled.connect(self.trackingChanged.emit) self.setlocationaction = self.toolbar.addAction("Set location") self.setlocationaction.setIcon(QIcon(":/icons/location")) self.setlocationaction.triggered.connect(self.setLocationTriggered.emit) self.setlocationaction.setCheckable(True) self.viewfeatures = self.toolbar.addAction("Load QGIS Features") self.viewfeatures.setIcon(QIcon(":/icons/features")) self.viewfeatures.setCheckable(True) self.viewfeatures.setChecked(True) self.viewfeatures.toggled.connect(self.updateFeatures.emit) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer) self.measureaction = self.toolbar.addAction("measure") self.measureaction.setObjectName("Measure") self.measureaction.setIcon(QIcon(":/icons/measure")) self.measureaction.setCheckable(True) self.infoaction = self.toolbar.addAction("Info") self.infoaction.setObjectName("Info") self.infoaction.setIcon(QIcon(":/icons/info")) self.infoaction.setCheckable(True) self.selectaction = self.toolbar.addAction("Select") self.selectaction.setObjectName("Select") self.selectaction.setIcon(QIcon(":/icons/select")) self.selectaction.setCheckable(True) self.toolbar.addSeparator() self.deleteaction = self.toolbar.addAction("Delete") self.deleteaction.setIcon(QIcon(":/icons/delete")) self.deleteaction.triggered.connect(self.delete_selected) self.deleteaction.setEnabled(False) self.addaction = self.toolbar.addAction("Add") self.addaction.setObjectName("Add") self.addaction.setIcon(QIcon(":/icons/add")) self.addaction.setCheckable(True) self.moveaction = self.toolbar.addAction("Move") self.moveaction.setObjectName("Move") self.moveaction.setIcon(QIcon(":/icons/move")) self.moveaction.setCheckable(True) self.actiongroup.addAction(self.moveaction) self.actiongroup.addAction(self.addaction) self.actiongroup.addAction(self.infoaction) self.actiongroup.addAction(self.measureaction) self.actiongroup.addAction(self.selectaction) self.activelayercombo = QgsMapLayerComboBox() self.activelayercombo.layerChanged.connect(self.layer_changed) self.activelayeraction = self.toolbar.addWidget(self.activelayercombo) self.activelayercombo.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.activelayercombo.currentIndexChanged.connect(self.index_changed) self.zvaluecheck = QCheckBox() self.zvaluecheck.setChecked(True) self.zvaluecheck.setText("Copy Z value") self.zvaluecheck.setToolTip("Copy Z value from viewer to new features in QGIS. Must have a field named Z to enable") self.zvalueaction = self.toolbar.addWidget(self.zvaluecheck) self.dockWidgetContents.layout().insertWidget(0, self.toolbar) self.webview.settings().setAttribute(QWebSettings.PluginsEnabled, True) self.webview.settings().setAttribute(QWebSettings.JavascriptEnabled, True) self.webview.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.frame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.frame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.frame.javaScriptWindowObjectCleared.connect(self.addcallbackobject) self.measuredialog.modeCombo.currentIndexChanged.connect(self.action_triggered) self.measuredialog.clearButton.clicked.connect(self.clear_line) self.earthmine = EarthmineAPI(self.frame) def closeEvent(self, event): self.closed.emit() super(Viewer, self).closeEvent(event) def index_changed(self, index): if index == -1: self.set_button_states(False, False, False, False) def clear_line(self): self.clearLine.emit() self.earthmine.clearLine() @property def copyZvalue(self): layer = self.active_layer if not layer: return False if layer.type() == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point: return self.zvaluecheck.isChecked() else: return False @property def geom(self): return self.measuredialog.geom @geom.setter def geom(self, value): self.measuredialog.geom = value self.measuredialog.update_geom_labels() @property def tracking(self): return self.qgisTrackButton.isChecked() @property def mode(self): return self.measuredialog.mode @property def active_layer(self): return self.activelayercombo.currentLayer() def layer_changed(self, layer): if not layer: self.set_button_states(False, False, False, False) return if layer.type() == QgsMapLayer.VectorLayer: enabledselecttools = layer.geometryType() in [QGis.Line, QGis.Point] enableedittools = layer.isEditable() enabledelete = layer.isEditable() and layer.selectedFeatureCount() enablemove = layer.geometryType() == QGis.Point and layer.isEditable() else: enabledselecttools = False enableedittools = False enabledelete = False enablemove = False self.set_button_states(enabledselecttools, enableedittools, enabledelete, enablemove) self.action_triggered() self.layerChanged.emit(layer) def selection_changed(self, layer): if layer == self.active_layer: enabledelete = layer.isEditable() and layer.selectedFeatureCount() self.deleteaction.setEnabled(enabledelete) def set_button_states(self, selecttools, edittools, deleteenabled, moveenabled): actions = [self.selectaction, self.infoaction] for action in actions: action.setEnabled(selecttools) editactions = [self.deleteaction, self.moveaction, self.addaction] for action in editactions: action.setEnabled(edittools) if edittools: self.deleteaction.setEnabled(deleteenabled) self.moveaction.setEnabled(moveenabled) for action in editactions: if action is self.actiongroup.checkedAction() and not action.isEnabled(): self.infoaction.toggle() break layer = self.active_layer if not layer: enablez = False else: enablez = layer.type() == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point self.zvalueaction.setEnabled(enablez) @property def current_action_color(self): action = self.actiongroup.checkedAction() color = int("0x00ff00", 16) if action == self.measureaction: if self.mode == "Vertical": color = int("0x0000ff", 16) return color def action_triggered(self, *args): action = self.actiongroup.checkedAction() layer = self.activelayercombo.currentLayer() self.clear_line() if not action: return if not action == self.measureaction and (not layer or not layer.type() == QgsMapLayer.VectorLayer): return color = self.current_action_color actiondata = {} if action == self.measureaction: self.measuredialog.show() actiondata['mode'] = self.mode geomtype = None layerid = None else: self.measuredialog.hide() geomtype = QGis.vectorGeometryType(layer.geometryType()) layerid = layer.id() data = dict(action=action.objectName(), layer=layerid, geom=geomtype, actiondata=actiondata, color=color) self.earthmine.updateAction(data) def active_tool(self): action = self.actiongroup.checkedAction() if not action: return None return action.objectName() def update_current_layer(self, layer): self.activelayercombo.setLayer(layer) def addcallbackobject(self): self.frame.addToJavaScriptWindowObject("qgis", self.callbackobject) def loadviewer(self, url): self.webview.load(url) self.frame.addToJavaScriptWindowObject("qgis", self.callbackobject) def startViewer(self, settings): self.earthmine.startViewer(settings) def set_location(self, point): # # NOTE Set location takes WGS84 make sure you have transformed it first before sending self.earthmine.setLocation(point.x(), point.y()) def clear_features(self): self.earthmine.clearFeatures() def clear_layer_features(self, layerid): self.earthmine.clearLayerObjects(layerid) def remove_feature(self, layerid, featureid): """ :param features: A dict of layerid, id, lat, lng :return: """ self.earthmine.removeFeature(layerid, featureid) def load_features(self, layerdata, features): """ :param features: A dict of layerid, id, lat, lng :return: """ self.earthmine.loadFeatures(layerdata, features) def layer_loaded(self, layerid): return self.earthmine.layerLoaded(layerid) def clear_selection(self, layerid): self.earthmine.clearSelection(layerid) def set_selection(self, layerid, featureids, clearlast=True): self.earthmine.setSelection(layerid, featureids, clearlast) def edit_feature(self, layerid, featureid, nodes): self.earthmine.editFeature(layerid, featureid, nodes) def delete_selected(self): layer = self.active_layer layer.deleteSelectedFeatures()
class UAVPreparer: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'UAVPreparer_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&UAV Preparer') # Check if plugin was started the first time in current QGIS session # Must be set in initGui() to survive plugin reloads self.first_start = None # Declare variables self.outputfile = None # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('UAVPreparer', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) 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: # Adds plugin icon to Plugins toolbar self.iface.addToolBarIcon(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/uav_preparer/icon.png' self.add_action(icon_path, text=self.tr(u'UAV Preparer'), callback=self.run, parent=self.iface.mainWindow()) # will be set False in run() self.first_start = True def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u'&UAV Preparer'), action) self.iface.removeToolBarIcon(action) def run(self): """Run method that performs all the real work""" # Create the dialog with elements (after translation) and keep reference # Only create GUI ONCE in callback, so that it will only load when the plugin is started if self.first_start == True: self.first_start = False self.dlg = UAVPreparerDialog() # Access the raster layer self.layerComboManagerDSM = QgsMapLayerComboBox(self.dlg.widgetDSM) self.layerComboManagerDSM.setFilters(QgsMapLayerProxyModel.RasterLayer) self.layerComboManagerDSM.setFixedWidth(175) self.layerComboManagerDSM.setCurrentIndex(-1) # Access the vector layer and an attribute field self.layerComboManagerPoint = QgsMapLayerComboBox( self.dlg.widgetPointLayer) self.layerComboManagerPoint.setCurrentIndex(-1) self.layerComboManagerPoint.setFilters( QgsMapLayerProxyModel.PointLayer) self.layerComboManagerPoint.setFixedWidth(175) self.layerComboManagerPointField = QgsFieldComboBox( self.dlg.widgetField) self.layerComboManagerPointField.setFilters(QgsFieldProxyModel.Numeric) self.layerComboManagerPoint.layerChanged.connect( self.layerComboManagerPointField.setLayer) # Set up of file save dialog self.fileDialog = QFileDialog() self.dlg.pushButtonSave.clicked.connect(self.savefile) # Set up for the Help button self.dlg.helpButton.clicked.connect(self.help) # Set up for the Run button self.dlg.runButton.clicked.connect(self.start_progress) # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result: # Do something useful here - delete the line containing pass and # substitute with your code. pass def savefile(self): self.outputfile = self.fileDialog.getSaveFileName( None, "Save File As:", None, "Text Files (*.txt)") self.dlg.textOutput.setText(self.outputfile[0]) def help(self): url = "https://github.com/biglimp/UAVPreparer" webbrowser.open_new_tab(url) def start_progress(self): if not self.outputfile: QMessageBox.critical(None, "Error", "Specify an output file") return # Acquiring geodata and attributes dsm_layer = self.layerComboManagerDSM.currentLayer() if dsm_layer is None: QMessageBox.critical(None, "Error", "No valid raster layer is selected") return else: provider = dsm_layer.dataProvider() filepath_dsm = str(provider.dataSourceUri()) point_layer = self.layerComboManagerPoint.currentLayer() if point_layer is None: QMessageBox.critical(None, "Error", "No valid vector point layer is selected") return else: vlayer = QgsVectorLayer(point_layer.source(), "polygon", "ogr") point_field = self.layerComboManagerPointField.currentField() idx = vlayer.fields().indexFromName(point_field) if idx == -1: QMessageBox.critical( None, "Error", "An attribute with unique fields must be selected") return ### main code ### # set radius r = 100 # half picture size numfeat = vlayer.featureCount() result = np.zeros([numfeat, 4]) # load big raster bigraster = gdal.Open(filepath_dsm) filepath_tempdsm = self.plugin_dir + '/clipdsm.tif' self.dlg.progressBar.setRange(0, numfeat) i = 0 for f in vlayer.getFeatures(): self.dlg.progressBar.setValue(i + 1) # get the coordinate for the point y = f.geometry().centroid().asPoint().y() x = f.geometry().centroid().asPoint().x() bbox = (x - r, y + r, x + r, y - r) gdal.Translate(filepath_tempdsm, bigraster, projWin=bbox) data = gdal.Open(filepath_tempdsm) mat = np.array(data.ReadAsArray()) result[i, 0] = int(f.attributes()[idx]) result[i, 1] = np.mean(mat) result[i, 2] = np.max(mat) result[i, 3] = np.min(mat) i = i + 1 # Saving to file numformat = '%d ' + '%6.2f ' * 3 headertext = 'id mean max min' np.savetxt(self.outputfile[0], result, fmt=numformat, delimiter=' ', header=headertext) self.iface.messageBar().pushMessage( 'UAV Preparer. Operation successful!', level=Qgis.Success, duration=5)