Esempio n. 1
0
    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 __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
Esempio n. 4
0
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