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', 'MultiRingBuffer_{}.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 = MultiRingBufferDialog() # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Multi Ring Buffer') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'MultiRingBuffer') self.toolbar.setObjectName(u'MultiRingBuffer')
class MultiRingBuffer: """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', 'MultiRingBuffer_{}.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 = MultiRingBufferDialog() # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Multi Ring Buffer') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'MultiRingBuffer') self.toolbar.setObjectName(u'MultiRingBuffer') # 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('MultiRingBuffer', 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.addPluginToVectorMenu( 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/Multi_Ring_Buffer/icon.svg' self.add_action(icon_path, text=self.tr(u'Multi Ring Buffer'), callback=self.run, parent=self.iface.mainWindow()) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginVectorMenu(self.tr(u'&Multi Ring Buffer'), action) self.iface.removeToolBarIcon(action) def dissolve(self, input_feats): # Function to dissolve input features to allow for buffering of multiple features feats = [] # Create and empty list of features and add all features to it. # We use feature 0 later and this ensures it exits. for each_feat in input_feats: feats.append(each_feat) # Do not run if geometry is empty, produce an error instead. if len(feats) > 0: # Need to create empty geometry to hold the dissolved features, we use the first feature to seed it. # Combine require a non-empty geometry to work (I could not get it to work). feat = feats[0] dissolved_geom = QgsGeometry() dissolved_geom = feat.geometry() # Progress bar for dissolving progressMessageBar = self.iface.messageBar().createMessage("Dissolving...") progress = QProgressBar() progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) progressMessageBar.layout().addWidget(progress) self.iface.messageBar().pushWidget(progressMessageBar, self.iface.messageBar().INFO) maximum_progress = len(feats) progress.setMaximum(maximum_progress) i = 0 # Run through the features and dissolve them all. for each_feat in feats: geom = each_feat.geometry() dissolved_geom = geom.combine(dissolved_geom) i = i + 1 progress.setValue(i + 1) return_f = QgsFeature() return_f.setGeometry(dissolved_geom) self.iface.messageBar().clearWidgets() return return_f else: QMessageBox.warning(self.iface.mainWindow(), "Warning", "No features to dissolve.", QMessageBox.Ok) return input_feats def run(self): # Plugin uses selected layer, hope to add layer selection later. active_vl = self.iface.activeLayer() # We check for selected features to auto-populate the "use selected feature" box in the dialog. sel_feats = '' if active_vl is not None: sel_feats = active_vl.selectedFeatures() result = 0 """Run method that performs all the real work""" # Logic if to run the dialog if active_vl is None: sel_feats = '' QMessageBox.warning(self.iface.mainWindow(), "Warning", "No layer selected.", QMessageBox.Ok) result = 0 elif active_vl.type() == QgsMapLayer.RasterLayer: sel_feats = '' QMessageBox.warning(self.iface.mainWindow(), "Warning", "Raster layer selected.", QMessageBox.Ok) result = 0 elif active_vl.type() == QgsMapLayer.PluginLayer: sel_feats = '' QMessageBox.warning(self.iface.mainWindow(), "Warning", "Plugin layer selected, please save as a regular layer and try again.", QMessageBox.Ok) result = 0 elif active_vl is not None: self.dlg.populatedialogue(active_vl.name()) if len(sel_feats) == 0: # If selected layer has selected features populate the box. # If not make it impossible to populate the box. self.dlg.selectedfeats(0) else: self.dlg.selectedfeats(1) self.dlg.show() result = self.dlg.exec_() else: sel_feats = '' QMessageBox.warning(self.iface.mainWindow(), "Warning", "Could not process layer.", QMessageBox.Ok) result = 0 # If ok was clicked in the dialog, continue: if result == 1: buffer_crs_object = self.iface.activeLayer().crs() # Check the current CRS of the layer buffer_crs = buffer_crs_object.authid() # Apply that to the created layer if recognised buffer_input_crs = "Polygon?crs=%s" % buffer_crs # Create empty memory vector layer for buffers layer_name = active_vl.name() vl = QgsVectorLayer(buffer_input_crs, "%s_MultiRingBuffer" % layer_name, "memory") vl_pr = vl.dataProvider() # Distance feature for buffer distance vl_pr.addAttributes([QgsField("distance", QVariant.String)]) vl.updateFields() # Switch between sequential and central buffer styles. # A toggle was added, but it seems too complex of a question for most users. buffer_style = "sequential" #if self.dlg.ui.central.isChecked(): # buffer_style = "central" #else: # buffer_style = "sequential" # Inputs from the dialog: dissolve_test = self.dlg.ui.dissovle_button_2.isChecked() if dissolve_test == 0: dissolve_bool = 0 else: dissolve_bool = 1 segments_to_approximate = self.dlg.ui.segmentsToApproximate.value() num_of_rings = self.dlg.ui.numberOfRings.value() buffer_distance = self.dlg.ui.bufferDistance.value() use_sel_feats_test = self.dlg.ui.selectedfeats.isChecked() if use_sel_feats_test == 0: use_sel_feats = 0 else: use_sel_feats = 1 # We use "sel_feats" to populate a box in the dialog, but if all features wanted, we need to re-populate it # with all the features in the layer. if use_sel_feats == 0: iterate = active_vl.getFeatures() sel_feats = [] for feature in iterate: sel_feats.append(feature) # Dissolve the features if selected. if dissolve_bool == 1: sel2feats = [] add_feat = self.dissolve(sel_feats) # Our buffer loops require a list (as sel_feats originally is), so we append the features to a list. sel2feats.append(add_feat) else: sel2feats = sel_feats # Progress bar. progressMessageBar = self.iface.messageBar().createMessage("Buffering...") progress = QProgressBar() progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) progressMessageBar.layout().addWidget(progress) self.iface.messageBar().pushWidget(progressMessageBar, self.iface.messageBar().INFO) maximum_progress = len(sel2feats) * num_of_rings progress.setMaximum(maximum_progress) i = 0 # Run if there are features in the layer if len(sel_feats) > 0: # Buffer a feature then buffer the buffer (used) if buffer_style == "sequential": new_buff_feats = [] distance = buffer_distance while num_of_rings > 0: to_buffer = [] for each_feat in sel2feats: geom = each_feat.geometry() buff = geom.buffer(buffer_distance, segments_to_approximate) new_f = QgsFeature() new_f.setGeometry(buff) to_buffer.append(new_f) new_f_geom = new_f.geometry() new_f_clipped = new_f_geom.difference(geom) new_f2 = QgsFeature() new_f2.setGeometry(new_f_clipped) new_f2.setAttributes([distance]) new_buff_feats.append(new_f2) i = i + 1 progress.setValue(i + 1) sel2feats = to_buffer num_of_rings = num_of_rings - 1 distance = distance + buffer_distance vl_pr.addFeatures(new_buff_feats) QgsMapLayerRegistry.instance().addMapLayer(vl) # Sequential buffers of the original feature with larger buffers if buffer_style == "central": orig_buffer_distance = buffer_distance buffered = [] for each_feat in sel2feats: num_to_buffer = num_of_rings to_clip = each_feat.geometry() buffer_distance = orig_buffer_distance while num_to_buffer > 0: geom = each_feat.geometry() buff = geom.buffer(buffer_distance, segments_to_approximate) new_f = QgsFeature() new_f_clipped = buff.difference(to_clip) new_f.setGeometry(new_f_clipped) new_f.setAttributes([buffer_distance]) buffered.append(new_f) buffer_distance = buffer_distance + orig_buffer_distance num_to_buffer = num_to_buffer - 1 to_clip = to_clip.combine(buff) i = i + 1 progress.setValue(i + 1) vl_pr.addFeatures(buffered) QgsMapLayerRegistry.instance().addMapLayer(vl) self.iface.messageBar().clearWidgets()