def __init__(self, iface): # 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] localePath = os.path.join(self.plugin_dir, 'i18n', 'concavehull_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = ConcaveHullDialog() # get reference to the QGIS message bar self.msg_bar = self.iface.messageBar() # Create the provider for Processing Toolbox self.provider = ConcaveHullProvider()
class ConcaveHull: def __init__(self, iface): # 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] localePath = os.path.join(self.plugin_dir, 'i18n', 'concavehull_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = ConcaveHullDialog() # get reference to the QGIS message bar self.msg_bar = self.iface.messageBar() # Create the provider for Processing Toolbox self.provider = ConcaveHullProvider() def initGui(self): """ Create action that will start plugin configuration """ self.action = QAction( QIcon(":/plugins/concavehull/icon.svg"), u"Concave Hull", 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.addPluginToVectorMenu(u"&Concave Hull", self.action) # Add algorithms to Processing Toolbox Processing.addProvider(self.provider) def unload(self): """ Remove the plugin menu item and icon """ self.iface.removePluginVectorMenu(u"&Concave Hull", self.action) self.iface.removeToolBarIcon(self.action) Processing.removeProvider(self.provider) def create_output_feature(self, geom, layer_name='ConcaveHull'): """ Creates a memory layer named layer_name, default name ConcaveHull, using project CRS and suppressing the CRS settings dialog :param geom: list of polygons :param layer_name: string :return: boolean """ if self.dlg.rb_shapefile.isChecked(): shape_filename = self.dlg.ed_output_layer.text() layer_name = 'ConcaveHull' if shape_filename == '': msg = self.msg_bar.createMessage(_translate('ConcaveHull', 'No shapefile name specified', None)) self.msg_bar.pushWidget(msg, QgsMessageBar.CRITICAL, 5) return False else: if self.dlg.rb_new_memory_layer.isChecked(): layer_name = self.dlg.ed_memory_layer.text() else: layer_name = self.dlg.cb_output.currentText() # if the layer does not exist it has to be created if not QgsMapLayerRegistry.instance().mapLayersByName(layer_name): srs = self.iface.mapCanvas().mapRenderer().destinationCrs().authid() layer = QgsVectorLayer('Polygon?crs=' + str(srs) + '&field=id:integer&field=count:integer', layer_name, 'memory') provider = layer.dataProvider() # if the layer already exists else: layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0] provider = layer.dataProvider() # add hull geometry to data provider fid = 0 for hull in geom: feature = QgsFeature() feature.setGeometry(hull[0]) if layer.fieldNameIndex('id') > -1: feature.setAttributes([fid, hull[1]]) fid += 1 provider.addFeatures([feature]) # if new memory layer simply add memory layer to the map if self.dlg.rb_new_memory_layer.isChecked(): QgsMapLayerRegistry.instance().addMapLayer(layer) # if features go to shapefile dump memory layer to shapefile elif self.dlg.rb_shapefile.isChecked(): error = QgsVectorFileWriter.writeAsVectorFormat(layer, shape_filename, 'CP1250', None, 'ESRI Shapefile') if error != QgsVectorFileWriter.NoError: msg = self.msg_bar.createMessage(_translate('ConcaveHull', 'Error writing shapefile: ' + str(error), None)) self.msg_bar.pushWidget(msg, QgsMessageBar.ERROR, 5) return False # add the new layer to the map if self.dlg.cb_add_to_map.isChecked(): base_name = os.path.splitext(os.path.basename(str(shape_filename)))[0] layer_name = QgsVectorLayer(shape_filename, base_name, 'ogr') QgsMapLayerRegistry.instance().addMapLayer(layer_name) # because change of extent in provider is not propagated to the layer layer.updateExtents() layer.triggerRepaint() return True def get_vector_layers_by_type(self, geom_type=None, skip_active=False): """ Returns a dict of layers [name: id] in the project for the given geom_type. If skip_active is True the active layer is not included. Code taken from DigitizingTools plugin, (C) 2013 by Bernhard Stroebl :param geom_type: integer; geomTypes are 0: point, 1: line, 2: polygon :return: dict of layers with given geometry type """ layer_list = {} for layer in self.iface.legendInterface().layers(): if 0 == layer.type(): # vectorLayer if skip_active and (self.iface.mapCanvas().currentLayer().id() == layer.id()): continue else: if geom_type is not None: if isinstance(geom_type, int): if layer.geometryType() == geom_type: layer_list[layer.name()] = layer.id() else: layer_list[layer.name()] = layer.id() return layer_list def set_output_layer_combobox(self, geom_type=None, item=''): """ Populates the ComboBox with all layers of the given geometry type geom_type, and sets currentIndex to the entry named index. :param geom_type: integer; geomTypes are 0: point, 1: line, 2: polygon :param index: string; name of the ComboBox entry to set currentIndex to :return: None """ self.dlg.cb_output.clear() layer_list = self.get_vector_layers_by_type(geom_type, False) if len(layer_list) > 0: for index, aName in enumerate(layer_list): self.dlg.cb_output.addItem('') self.dlg.cb_output.setItemText(index, aName) if aName == item: self.dlg.cb_output.setCurrentIndex(index) return None # run method that performs all the real work def run(self): # set dialog widgets self.dlg.ls_layers.clear() self.dlg.buttonBox.button(QDialogButtonBox.Ok).setDisabled(True) has_selected_features = False # check if an active layer exists active_layer_name = '' if self.iface.activeLayer() is not None: active_layer_name = self.iface.activeLayer().name() # all vector layers get added to the list for index, layer in enumerate(self.iface.legendInterface().layers()): if layer.type() == QgsMapLayer.VectorLayer: # if there are selected features toggle has_selected_features if layer.selectedFeatureCount(): has_selected_features = True self.dlg.ls_layers.addItem(layer.name()) # select the active layer by default if layer.name() == active_layer_name: self.dlg.ls_layers.setCurrentRow(index) # if at least one vector layer has selected features enable checkbutton cb_selected_only if has_selected_features: self.dlg.cb_selected_only.setEnabled(True) self.dlg.cb_selected_only.setChecked(True) else: self.dlg.cb_selected_only.setChecked(False) self.dlg.cb_selected_only.setEnabled(False) # initialize cb_output # remember the layer being selected the last time last_index = self.dlg.cb_output.currentText() # populate the combo box with the polygon layers listed in the current legend self.set_output_layer_combobox(2, last_index) # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result == 1: geom = [] # read selected list entries and get the map layer for layer_name in self.dlg.ls_layers.selectedItems(): active_layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name.text())[0] # get all or the currently selected features according to state of cb_selected_only # convert each feature to points if active_layer.selectedFeatureCount() and self.dlg.cb_selected_only.checkState(): for feat in active_layer.selectedFeatures(): geom.extend(extract_points(feat.geometry())) else: for feat in active_layer.getFeatures(): geom.extend(extract_points(feat.geometry())) num_points = len(geom) if num_points == 0: return None # Send WARNING to the message bar to inform about a probably long running time if num_points > 1000: msg = self.msg_bar.createMessage(u'Please be patient, processing of more then {} points may take a while'. format(int(num_points))) self.msg_bar.pushWidget(msg, QgsMessageBar.WARNING, 2) # if more then 5000 points ask user to confirm if num_points > 5000: proceed = QMessageBox.question(None, 'Please confirm', 'Do you really want to proceed?', QMessageBox.Yes | QMessageBox.No) if proceed == QMessageBox.No: QApplication.instance().setOverrideCursor(Qt.ArrowCursor) return None # change cursor to inform user about ongoing processing QApplication.instance().setOverrideCursor(Qt.BusyCursor) # generate the hull geometry # process points with prior clustering hull_list = [] if self.dlg.gb_clustering.isChecked(): clusters = SSNClusters(geom, self.dlg.sb_neighborhood_list_size.value()).get_clusters() for cluster in clusters.keys(): the_hull = concave_hull(clusters[cluster], self.dlg.sb_neighbors.value()) if the_hull: hull_list.append([as_polygon(the_hull), len(clusters[cluster])]) else: # process points without clustering the_hull = concave_hull(geom, self.dlg.sb_neighbors.value()) hull_list.append([as_polygon(the_hull), len(geom)]) # write hull geometries to output device success = self.create_output_feature(hull_list) # report result in QGIS message bar if success: msg = self.msg_bar.createMessage(_translate('ConcaveHull', '{} Concave hulls created successfully'. format(int(len(hull_list))), None)) self.msg_bar.pushWidget(msg, QgsMessageBar.INFO, 5) # reset cursor QApplication.instance().setOverrideCursor(Qt.ArrowCursor) return None
class ConcaveHull: def __init__(self, iface): # 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] localePath = os.path.join(self.plugin_dir, 'i18n', 'concavehull_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = ConcaveHullDialog() # get reference to the QGIS message bar self.msg_bar = self.iface.messageBar() # Create the provider for Processing Toolbox self.provider = ConcaveHullProvider() def initGui(self): """ Create action that will start plugin configuration """ self.action = QAction(QIcon(":/plugins/concavehull/icon.svg"), u"Concave Hull", 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.addPluginToVectorMenu(u"&Concave Hull", self.action) # Add algorithms to Processing Toolbox Processing.addProvider(self.provider) def unload(self): """ Remove the plugin menu item and icon """ self.iface.removePluginVectorMenu(u"&Concave Hull", self.action) self.iface.removeToolBarIcon(self.action) Processing.removeProvider(self.provider) def create_output_feature(self, geom, layer_name='ConcaveHull'): """ Creates a memory layer named layer_name, default name ConcaveHull, using project CRS and suppressing the CRS settings dialog :param geom: list of polygons :param layer_name: string :return: boolean """ if self.dlg.rb_shapefile.isChecked(): shape_filename = self.dlg.ed_output_layer.text() layer_name = 'ConcaveHull' if shape_filename == '': msg = self.msg_bar.createMessage( _translate('ConcaveHull', 'No shapefile name specified', None)) self.msg_bar.pushWidget(msg, QgsMessageBar.CRITICAL, 5) return False else: if self.dlg.rb_new_memory_layer.isChecked(): layer_name = self.dlg.ed_memory_layer.text() else: layer_name = self.dlg.cb_output.currentText() # if the layer does not exist it has to be created if not QgsMapLayerRegistry.instance().mapLayersByName(layer_name): srs = self.iface.mapCanvas().mapRenderer().destinationCrs().authid( ) layer = QgsVectorLayer( 'Polygon?crs=' + str(srs) + '&field=id:integer&field=count:integer', layer_name, 'memory') provider = layer.dataProvider() # if the layer already exists else: layer = QgsMapLayerRegistry.instance().mapLayersByName( layer_name)[0] provider = layer.dataProvider() # add hull geometry to data provider fid = 0 for hull in geom: feature = QgsFeature() feature.setGeometry(hull[0]) if layer.fieldNameIndex('id') > -1: feature.setAttributes([fid, hull[1]]) fid += 1 provider.addFeatures([feature]) # if new memory layer simply add memory layer to the map if self.dlg.rb_new_memory_layer.isChecked(): QgsMapLayerRegistry.instance().addMapLayer(layer) # if features go to shapefile dump memory layer to shapefile elif self.dlg.rb_shapefile.isChecked(): error = QgsVectorFileWriter.writeAsVectorFormat( layer, shape_filename, 'CP1250', None, 'ESRI Shapefile') if error != QgsVectorFileWriter.NoError: msg = self.msg_bar.createMessage( _translate('ConcaveHull', 'Error writing shapefile: ' + str(error), None)) self.msg_bar.pushWidget(msg, QgsMessageBar.ERROR, 5) return False # add the new layer to the map if self.dlg.cb_add_to_map.isChecked(): base_name = os.path.splitext( os.path.basename(str(shape_filename)))[0] layer_name = QgsVectorLayer(shape_filename, base_name, 'ogr') QgsMapLayerRegistry.instance().addMapLayer(layer_name) # because change of extent in provider is not propagated to the layer layer.updateExtents() layer.triggerRepaint() return True def get_vector_layers_by_type(self, geom_type=None, skip_active=False): """ Returns a dict of layers [name: id] in the project for the given geom_type. If skip_active is True the active layer is not included. Code taken from DigitizingTools plugin, (C) 2013 by Bernhard Stroebl :param geom_type: integer; geomTypes are 0: point, 1: line, 2: polygon :return: dict of layers with given geometry type """ layer_list = {} for layer in self.iface.legendInterface().layers(): if 0 == layer.type(): # vectorLayer if skip_active and (self.iface.mapCanvas().currentLayer().id() == layer.id()): continue else: if geom_type is not None: if isinstance(geom_type, int): if layer.geometryType() == geom_type: layer_list[layer.name()] = layer.id() else: layer_list[layer.name()] = layer.id() return layer_list def set_output_layer_combobox(self, geom_type=None, item=''): """ Populates the ComboBox with all layers of the given geometry type geom_type, and sets currentIndex to the entry named index. :param geom_type: integer; geomTypes are 0: point, 1: line, 2: polygon :param index: string; name of the ComboBox entry to set currentIndex to :return: None """ self.dlg.cb_output.clear() layer_list = self.get_vector_layers_by_type(geom_type, False) if len(layer_list) > 0: for index, aName in enumerate(layer_list): self.dlg.cb_output.addItem('') self.dlg.cb_output.setItemText(index, aName) if aName == item: self.dlg.cb_output.setCurrentIndex(index) return None # run method that performs all the real work def run(self): # set dialog widgets self.dlg.ls_layers.clear() self.dlg.buttonBox.button(QDialogButtonBox.Ok).setDisabled(True) has_selected_features = False # check if an active layer exists active_layer_name = '' if self.iface.activeLayer() is not None: active_layer_name = self.iface.activeLayer().name() # all vector layers get added to the list for index, layer in enumerate(self.iface.legendInterface().layers()): if layer.type() == QgsMapLayer.VectorLayer: # if there are selected features toggle has_selected_features if layer.selectedFeatureCount(): has_selected_features = True self.dlg.ls_layers.addItem(layer.name()) # select the active layer by default if layer.name() == active_layer_name: self.dlg.ls_layers.setCurrentRow(index) # if at least one vector layer has selected features enable checkbutton cb_selected_only if has_selected_features: self.dlg.cb_selected_only.setEnabled(True) self.dlg.cb_selected_only.setChecked(True) else: self.dlg.cb_selected_only.setChecked(False) self.dlg.cb_selected_only.setEnabled(False) # initialize cb_output # remember the layer being selected the last time last_index = self.dlg.cb_output.currentText() # populate the combo box with the polygon layers listed in the current legend self.set_output_layer_combobox(2, last_index) # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result == 1: geom = [] # read selected list entries and get the map layer for layer_name in self.dlg.ls_layers.selectedItems(): active_layer = QgsMapLayerRegistry.instance().mapLayersByName( layer_name.text())[0] # get all or the currently selected features according to state of cb_selected_only # convert each feature to points if active_layer.selectedFeatureCount( ) and self.dlg.cb_selected_only.checkState(): for feat in active_layer.selectedFeatures(): geom.extend(extract_points(feat.geometry())) else: for feat in active_layer.getFeatures(): geom.extend(extract_points(feat.geometry())) num_points = len(geom) if num_points == 0: return None # Send WARNING to the message bar to inform about a probably long running time if num_points > 1000: msg = self.msg_bar.createMessage( u'Please be patient, processing of more then {} points may take a while' .format(int(num_points))) self.msg_bar.pushWidget(msg, QgsMessageBar.WARNING, 2) # if more then 5000 points ask user to confirm if num_points > 5000: proceed = QMessageBox.question( None, 'Please confirm', 'Do you really want to proceed?', QMessageBox.Yes | QMessageBox.No) if proceed == QMessageBox.No: QApplication.instance().setOverrideCursor(Qt.ArrowCursor) return None # change cursor to inform user about ongoing processing QApplication.instance().setOverrideCursor(Qt.BusyCursor) # generate the hull geometry # process points with prior clustering hull_list = [] if self.dlg.gb_clustering.isChecked(): clusters = SSNClusters( geom, self.dlg.sb_neighborhood_list_size.value()).get_clusters() for cluster in clusters.keys(): the_hull = concave_hull(clusters[cluster], self.dlg.sb_neighbors.value()) if the_hull: hull_list.append( [as_polygon(the_hull), len(clusters[cluster])]) else: # process points without clustering the_hull = concave_hull(geom, self.dlg.sb_neighbors.value()) hull_list.append([as_polygon(the_hull), len(geom)]) # write hull geometries to output device success = self.create_output_feature(hull_list) # report result in QGIS message bar if success: msg = self.msg_bar.createMessage( _translate( 'ConcaveHull', '{} Concave hulls created successfully'.format( int(len(hull_list))), None)) self.msg_bar.pushWidget(msg, QgsMessageBar.INFO, 5) # reset cursor QApplication.instance().setOverrideCursor(Qt.ArrowCursor) return None