コード例 #1
0
ファイル: copy.py プロジェクト: gvellut/GVDigitizingTools
    def __init__(self, iface, digitizingTools):
        self.iface = iface
        self.digitizingTools = digitizingTools
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandMoveAxis = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandMoveAxis.setColor(QColor(255, 0, 0))
        self.rubberBandMoveAxis.setWidth(2)

        self.rubberBandGeometriesPreview = QgsRubberBand(
            self.iface.mapCanvas())
        self.rubberBandGeometriesPreview.setColor(QColor(0, 255, 0))
        self.rubberBandGeometriesPreview.setWidth(1)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        self.isEmittingPoint = False

        # we need a snapper, so we use the MapCanvas snapper
        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()
コード例 #2
0
ファイル: fillet.py プロジェクト: gvellut/GVDigitizingTools
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)
        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())
        self.rubberBandSegment1 = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSegment1.setColor(QColor(255, 0, 0))
        self.rubberBandSegment1.setWidth(2)

        self.layer = None
        self.reset()
コード例 #3
0
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()
コード例 #4
0
ファイル: scale.py プロジェクト: gvellut/GVDigitizingTools
    def __init__(self, iface, digitizingTools):
        self.iface = iface
        self.digitizingTools = digitizingTools
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        # we need a snapper, so we use the MapCanvas snapper
        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()
コード例 #5
0
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)
        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())

        # just needs to rubberbands (when selecting 2nd segment, replaced by selectedLine or
        # hidden)
        self.rubberBandSelectedSegment = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedSegment.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedSegment.setWidth(2)

        self.rubberBandSelectedLine = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedLine.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedLine.setWidth(2)

        self.defaultIndex = None
        self.layer = None
        self.reset()
コード例 #6
0
ファイル: copy.py プロジェクト: gvellut/GVDigitizingTools
class CopyDigitizingMode(QgsMapToolEmitPoint):

    MESSAGE_HEADER = "Copy"

    def __init__(self, iface, digitizingTools):
        self.iface = iface
        self.digitizingTools = digitizingTools
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandMoveAxis = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandMoveAxis.setColor(QColor(255, 0, 0))
        self.rubberBandMoveAxis.setWidth(2)

        self.rubberBandGeometriesPreview = QgsRubberBand(
            self.iface.mapCanvas())
        self.rubberBandGeometriesPreview.setColor(QColor(0, 255, 0))
        self.rubberBandGeometriesPreview.setWidth(1)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        self.isEmittingPoint = False

        # we need a snapper, so we use the MapCanvas snapper
        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

        crsDest = self.layer.crs()
        canvas = self.iface.mapCanvas()
        mapRenderer = canvas.mapSettings()
        crsSrc = mapRenderer.destinationCrs()
        self.crsTransform = QgsCoordinateTransform(crsSrc, crsDest)
        # cannot indicate direction in QgsGeometry.transform
        self.crsTransformReverse = QgsCoordinateTransform(crsDest, crsSrc)

    def reset(self, clearMessages=True):
        self.step = 0

        self.rubberBandMoveAxis.reset(QGis.Line)
        self.rubberBandGeometriesPreview.reset(QGis.Line)
        self.rubberBandSnap.reset(QGis.Point)

        settings = QSettings()
        self.isPreviewEnabled = settings.value(constants.PREVIEW_ENABLED,
                                               type=bool)
        self.isLimitPreviewEnabled = settings.value(
            constants.PREVIEW_LIMIT_ENABLED, type=bool)
        self.maxLimitPreview = settings.value(constants.PREVIEW_LIMIT_MAX,
                                              type=int)

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def deactivate(self):
        self.reset()
        super(QgsMapToolEmitPoint, self).deactivate()

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showMessage(
                CopyDigitizingMode.MESSAGE_HEADER,
                "Select base point of displacement",
                duration=0)
        elif self.step == 1:
            self.messageBarUtils.showButton(
                CopyDigitizingMode.MESSAGE_HEADER,
                "Select second point of displacement",
                "Done",
                buttonCallback=self.done)
        elif self.step == 2:
            if self.layer.selectedFeatureCount() == 0:
                self.messageBarUtils.showYesCancel(
                    CopyDigitizingMode.MESSAGE_HEADER,
                    "No feature selected in Layer %s. Proceed on full layer?" %
                    self.layer.name(), QgsMessageBar.WARNING, self.doCopy,
                    self.deactivate)
                return

            self.doCopy()

    def doCopy(self):
        _, progress = self.messageBarUtils.showProgress(
            CopyDigitizingMode.MESSAGE_HEADER, "Running...",
            QgsMessageBar.INFO)

        try:

            self.rubberBandGeometriesPreview.reset(QGis.Line)

            self.layer.beginEditCommand(CopyDigitizingMode.MESSAGE_HEADER)

            ok = self.prepareDisplacement()

            if not ok:
                self.messageBarUtils.showMessage(
                    CopyDigitizingMode.MESSAGE_HEADER,
                    "Invalid displacement.",
                    QgsMessageBar.WARNING,
                    duration=5)
                self.layer.destroyEditCommand()
                return

            self.iface.mapCanvas().freeze(True)

            outFeat = QgsFeature()

            current = 0
            features = vectorlayerutils.features(self.layer)
            total = 100.0 / float(len(features))
            pgidIndex = self.layer.fieldNameIndex("gid")
            for f in features:
                inGeom = f.geometry()
                attrs = f.attributes()

                # perform displacement in map coordinates instead of layer coordinates
                inGeom.transform(self.crsTransformReverse)
                outGeom = self.movePolyline(inGeom)
                outGeom.transform(self.crsTransform)

                outFeat.initAttributes(len(attrs))
                for index in range(len(attrs)):
                    if index <> pgidIndex:
                        outFeat.setAttribute(index, attrs[index])
                outFeat.setGeometry(outGeom)
                self.layer.addFeature(outFeat)

                current += 1
                progress.setValue(int(current * total))

            self.messageBarUtils.showMessage(CopyDigitizingMode.MESSAGE_HEADER,
                                             "Success", QgsMessageBar.INFO, 5)
            self.layer.endEditCommand()

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                CopyDigitizingMode.MESSAGE_HEADER,
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.layer.destroyEditCommand()

        finally:
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()

        self.resetCopy()
        self.next()

    def resetCopy(self):
        self.step = 1

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def prepareDisplacement(self):
        # rubber bands are in crs of map canvas
        self.dx = self.endPoint.x() - self.startPoint.x()
        self.dy = self.endPoint.y() - self.startPoint.y()
        return True

    def movePolyline(self, geom):
        geom.translate(self.dx, self.dy)
        return geom

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 1:
                self.done()
        elif e.button() == Qt.LeftButton:
            if self.step == 0:
                modifiers = e.modifiers()
                if modifiers == Qt.ControlModifier:
                    self.isDuplicate = True

                # we snap to the current layer (we don't have exclude points and use the tolerances from the qgis properties)
                pos = QPoint(e.pos().x(), e.pos().y())
                result = self.snap(pos)
                if result != []:
                    self.startPoint = QgsPoint(result[0].snappedVertex)
                else:
                    self.startPoint = self.toMapCoordinates(e.pos())

                self.endPoint = self.startPoint
                self.isEmittingPoint = True

                self.rubberBandMoveAxis.reset(QGis.Line)
                self.rubberBandMoveAxis.addPoint(self.startPoint)
                self.rubberBandMoveAxis.addPoint(self.endPoint)
                self.rubberBandMoveAxis.show()

                self.step = 1
                self.next()
            elif self.step == 1:
                pos = QPoint(e.pos().x(), e.pos().y())
                result = self.snap(pos)
                if result != []:
                    self.endPoint = QgsPoint(result[0].snappedVertex)
                else:
                    self.endPoint = self.toMapCoordinates(e.pos())

                self.step = 2
                self.next()

    def canvasReleaseEvent(self, e):
        pass

    def canvasMoveEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        self.rubberBandSnap.reset(QGis.Point)
        if result != []:
            self.rubberBandSnap.addPoint(result[0].snappedVertex, True)

        if not self.isEmittingPoint:
            return

        if self.step == 1:
            if result != []:
                self.endPoint = QgsPoint(result[0].snappedVertex)
            else:
                self.endPoint = self.toMapCoordinates(e.pos())
            self.rubberBandMoveAxis.reset(QGis.Line)
            self.rubberBandMoveAxis.addPoint(self.startPoint)
            self.rubberBandMoveAxis.addPoint(self.endPoint)
            self.rubberBandMoveAxis.show()

            self.updateGeometriesPreview()

    def updateGeometriesPreview(self):
        ok = self.prepareDisplacement()
        self.rubberBandGeometriesPreview.reset(QGis.Line)

        if not ok:
            return

        if not self.isPreviewEnabled:
            return

        if self.isLimitPreviewEnabled:
            maxCount = self.maxLimitPreview
        else:
            maxCount = self.layer.featureCount()

        currentCount = 0
        features = vectorlayerutils.features(self.layer)
        for f in features:
            inGeom = f.geometry()
            inGeom.transform(self.crsTransformReverse)
            inGeom = self.movePolyline(inGeom)
            self.rubberBandGeometriesPreview.addGeometry(inGeom, None)

            currentCount += 1
            if currentCount >= maxCount:
                break

        self.rubberBandGeometriesPreview.show()

    def snap(self, pos):
        if self.digitizingTools.isBackgroundSnapping:
            (_, result) = self.snapper.snapToBackgroundLayers(pos)
        else:
            (_,
             result) = self.snapper.snapToCurrentLayer(pos,
                                                       QgsSnapper.SnapToVertex)
        return result
コード例 #7
0
ファイル: explode.py プロジェクト: gvellut/GVDigitizingTools
 def __init__(self, iface):
     self.iface = iface
     self.messageBarUtils = MessageBarUtils(iface)
コード例 #8
0
ファイル: explode.py プロジェクト: gvellut/GVDigitizingTools
class ExplodeCommand(object):
    def __init__(self, iface):
        self.iface = iface
        self.messageBarUtils = MessageBarUtils(iface)

    def run(self):
        self.vlayer = self.iface.legendInterface().currentLayer()

        if self.vlayer.selectedFeatureCount() == 0:
            self.messageBarUtils.showYesCancel(
                "Explode",
                "No feature selected in Layer %s. Proceed on full layer?" %
                self.vlayer.name(), QgsMessageBar.WARNING, self.explode)
            return

        # run directly without asking anything
        self.explode()

    def explode(self):
        self.vlayer.beginEditCommand("Explode")

        _, progress = self.messageBarUtils.showProgress(
            "Explode", "Running...", QgsMessageBar.INFO)

        try:
            self.iface.mapCanvas().freeze(True)

            outFeat = QgsFeature()
            inGeom = QgsGeometry()

            current = 0
            features = vectorlayerutils.features(self.vlayer)
            total = 100.0 / float(len(features))
            for f in features:
                inGeom = f.geometry()
                attrs = f.attributes()

                geometries = self.extractAsSingle(inGeom)
                outFeat.setAttributes(attrs)

                for g in geometries:
                    outFeat.setGeometry(g)
                    self.vlayer.addFeature(outFeat)

                # delete original feature
                self.vlayer.deleteFeature(f.id())

                current += 1
                progress.setValue(int(current * total))

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.removeAllMessages()
            self.messageBarUtils.showMessage(
                "Explode",
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)
            if self.vlayer.isEditable():
                self.vlayer.destroyEditCommand()

            return
        finally:
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()

        # if here: Success!
        self.messageBarUtils.removeAllMessages()
        self.messageBarUtils.showMessage("Explode", "Success",
                                         QgsMessageBar.INFO, 5)

        self.vlayer.endEditCommand()

    def extractAsSingle(self, geom):
        multiGeom = QgsGeometry()
        geometries = []
        if geom.type() == QGis.Point:
            if geom.isMultipart():
                multiGeom = geom.asMultiPoint()
                for i in multiGeom:
                    geometries.append(QgsGeometry().fromPoint(i))
            else:
                geometries.append(geom)
        elif geom.type() == QGis.Line:
            if geom.isMultipart():
                multiGeom = geom.asMultiPolyline()
                for i in multiGeom:
                    geometries.append(QgsGeometry().fromPolyline(i))
            else:
                geometries.append(geom)
        elif geom.type() == QGis.Polygon:
            if geom.isMultipart():
                multiGeom = geom.asMultiPolygon()
                for i in multiGeom:
                    geometries.append(QgsGeometry().fromPolygon(i))
            else:
                geometries.append(geom)
        return geometries
コード例 #9
0
class CopyMultipleLayersDigitizingMode(QgsMapToolEmitPoint):

    MESSAGE_HEADER = "Copy from multiple layers"

    def __init__(self, iface, digitizingTools):
        self.iface = iface
        self.digitizingTools = digitizingTools
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandMoveAxis = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandMoveAxis.setColor(QColor(255, 0, 0))
        self.rubberBandMoveAxis.setWidth(2)

        self.rubberBandGeometriesPreview = QgsRubberBand(
            self.iface.mapCanvas())
        self.rubberBandGeometriesPreview.setColor(QColor(0, 255, 0))
        self.rubberBandGeometriesPreview.setWidth(1)

        self.rubberBandPointsPreview = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandPointsPreview.setColor(QColor(0, 255, 0))
        self.rubberBandPointsPreview.setWidth(2)
        self.rubberBandPointsPreview.setIcon(QgsRubberBand.ICON_CIRCLE)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        self.isEmittingPoint = False

        # we need a snapper, so we use the MapCanvas snapper
        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.reset()

    def setLayer(self, layer):
        # this mode ignores the currently selected layer
        pass

    def reset(self, clearMessages=True):
        self.step = 0

        self.rubberBandMoveAxis.reset(QGis.Line)
        self.rubberBandGeometriesPreview.reset(QGis.Line)
        self.rubberBandPointsPreview.reset(QGis.Point)
        self.rubberBandSnap.reset(QGis.Point)

        settings = QSettings()
        self.isPreviewEnabled = settings.value(constants.PREVIEW_ENABLED,
                                               type=bool)
        self.isLimitPreviewEnabled = settings.value(
            constants.PREVIEW_LIMIT_ENABLED, type=bool)
        self.maxLimitPreview = settings.value(constants.PREVIEW_LIMIT_MAX,
                                              type=int)

        self.isEmittingPoint = False

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def deactivate(self):
        self.reset()
        super(QgsMapToolEmitPoint, self).deactivate()

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showMessage(
                self.MESSAGE_HEADER,
                "Select base point of displacement",
                duration=0)
        elif self.step == 1:
            self.messageBarUtils.removeAllMessages()
            self.messageBarUtils.showButton(
                self.MESSAGE_HEADER,
                "Select second point of displacement",
                "Done",
                buttonCallback=self.done)
        elif self.step == 2:
            totalLayers = 0
            layersWithNoSelection = 0

            layers = self.iface.legendInterface().selectedLayers()
            for layer in layers:
                if layer.type() == QgsMapLayer.VectorLayer:
                    totalLayers += 1
                    if layer.selectedFeatureCount() == 0:
                        layersWithNoSelection += 1

            if totalLayers == layersWithNoSelection:
                self.messageBarUtils.showMessage(
                    self.MESSAGE_HEADER,
                    "None of the layers selected in the TOC has a selection. Nothing done",
                    QgsMessageBar.WARNING)
                self.iface.mapCanvas().refresh()
                self.iface.mapCanvas().unsetMapTool(self)
                return
            elif layersWithNoSelection != 0:
                self.messageBarUtils.showYesCancel(
                    self.MESSAGE_HEADER,
                    "%d out of %d vector layers have no selection. Proceed on the other layers?"
                    % (layersWithNoSelection, totalLayers),
                    QgsMessageBar.WARNING, self.doMove, self.deactivate)
                return

            self.doCopy()

    def doCopy(self):
        _, progress = self.messageBarUtils.showProgress(
            self.MESSAGE_HEADER, "Running...", QgsMessageBar.INFO)

        self.rubberBandGeometriesPreview.reset(QGis.Line)
        self.rubberBandPointsPreview.reset(QGis.Point)

        currentLayer = None
        isEditable = []

        try:
            ok = self.prepareDisplacement()
            if not ok:
                self.messageBarUtils.showMessage(self.MESSAGE_HEADER,
                                                 "Invalid displacement.",
                                                 QgsMessageBar.WARNING,
                                                 duration=5)
                return

            self.iface.mapCanvas().freeze(True)

            inGeom = QgsGeometry()
            outFeat = QgsFeature()

            current = 0

            layers = self.iface.legendInterface().selectedLayers()
            for layer in layers:
                # only deal with selected features of the selected layers
                if layer.type(
                ) == QgsMapLayer.VectorLayer and layer.selectedFeatureCount(
                ) > 0:
                    currentLayer = layer
                    if layer.isEditable():
                        isEditable.append(True)
                        layer.beginEditCommand("Copy from multiple layers")
                    else:
                        isEditable.append(False)
                        layer.startEditing()

                    crsDest = layer.crs()
                    canvas = self.iface.mapCanvas()
                    mapRenderer = canvas.mapSettings()
                    crsSrc = mapRenderer.destinationCrs()
                    crsTransform = QgsCoordinateTransform(crsSrc, crsDest)
                    # cannot indicate direction in QgsGeometry.transform
                    crsTransformReverse = QgsCoordinateTransform(
                        crsDest, crsSrc)

                    features = vectorlayerutils.features(layer)
                    total = 100.0 / float(len(features))
                    pgidIndex = layer.fieldNameIndex("gid")
                    progress.setValue(0)
                    for f in features:
                        inGeom = f.geometry()
                        attrs = f.attributes()

                        # perform displacement in map coordinates instead of layer coordinates
                        inGeom.transform(crsTransformReverse)
                        outGeom = self.moveGeometry(inGeom)
                        outGeom.transform(crsTransform)

                        outFeat.initAttributes(len(attrs))
                        for index in range(len(attrs)):
                            if index <> pgidIndex:
                                outFeat.setAttribute(index, attrs[index])
                        outFeat.setGeometry(outGeom)
                        layer.addFeature(outFeat)

                        current += 1
                        progress.setValue(int(current * total))

            currentLayer = None
            self.messageBarUtils.showMessage(self.MESSAGE_HEADER, "Success",
                                             QgsMessageBar.INFO, 5)
            # commit modifications in all layers at the end
            count = 0
            layers = self.iface.legendInterface().selectedLayers()
            for layer in layers:
                # only deal with selected features of the selected layers
                if layer.type(
                ) == QgsMapLayer.VectorLayer and layer.selectedFeatureCount(
                ) > 0:
                    if isEditable[count]:
                        layer.endEditCommand()
                    else:
                        layer.commitChanges()

                    count += 1

        except Exception as e:
            raise
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                self.MESSAGE_HEADER,
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)

            # rollback modifications in all layers
            count = 0
            layers = self.iface.legendInterface().selectedLayers()
            for layer in layers:
                # only deal with selected features of the selected layers
                if layer.type(
                ) == QgsMapLayer.VectorLayer and layer.selectedFeatureCount(
                ) > 0:
                    if isEditable[count]:
                        layer.destroyEditCommand()
                    else:
                        layer.rollBack()
                    # subsequent layers have not been modified
                    if layer == currentLayer:
                        break

                    count += 1

        finally:
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()

        self.resetCopyMultipleLayers()
        self.next()

    def resetCopyMultipleLayers(self):
        self.step = 1

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def prepareDisplacement(self):
        # rubber bands are in crs of map canvas
        self.dx = self.endPoint.x() - self.startPoint.x()
        self.dy = self.endPoint.y() - self.startPoint.y()
        return True

    def moveGeometry(self, geom):
        geom.translate(self.dx, self.dy)
        return geom

    def moveVertex(self, vertex):
        vertex.setX(vertex.x() + self.dx)
        vertex.setY(vertex.y() + self.dy)
        return vertex

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        pass

    def canvasReleaseEvent(self, e):
        if e.button() == Qt.RightButton:
            if self.step == 1:
                self.done()

        if self.step == 0:

            # we snap to the current layer (we don't have exclude points and use the tolerances from the qgis properties)
            pos = QPoint(e.pos().x(), e.pos().y())
            result = self.snap(pos)
            if result != []:
                self.startPoint = QgsPoint(result[0].snappedVertex)
            else:
                self.startPoint = self.toMapCoordinates(e.pos())

            self.endPoint = self.startPoint
            self.isEmittingPoint = True

            self.rubberBandMoveAxis.reset(QGis.Line)
            self.rubberBandMoveAxis.addPoint(self.startPoint)
            self.rubberBandMoveAxis.addPoint(self.endPoint)
            self.rubberBandMoveAxis.show()

            self.step = 1
            self.next()
        elif self.step == 1:
            pos = QPoint(e.pos().x(), e.pos().y())
            result = self.snap(pos)
            if result != []:
                self.endPoint = QgsPoint(result[0].snappedVertex)
            else:
                self.endPoint = self.toMapCoordinates(e.pos())

            self.step = 2
            self.next()

    def canvasMoveEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        self.rubberBandSnap.reset(QGis.Point)
        if result != []:
            self.rubberBandSnap.addPoint(result[0].snappedVertex, True)

        if not self.isEmittingPoint:
            return

        if self.step == 1:
            if result != []:
                self.endPoint = QgsPoint(result[0].snappedVertex)
            else:
                self.endPoint = self.toMapCoordinates(e.pos())
            self.rubberBandMoveAxis.reset(QGis.Line)
            self.rubberBandMoveAxis.addPoint(self.startPoint)
            self.rubberBandMoveAxis.addPoint(self.endPoint)
            self.rubberBandMoveAxis.show()

            self.updateGeometriesPreview()

    def updateGeometriesPreview(self):
        ok = self.prepareDisplacement()
        self.rubberBandGeometriesPreview.reset(QGis.Line)
        self.rubberBandPointsPreview.reset(QGis.Point)

        if not ok:
            return

        if not self.isPreviewEnabled:
            return

        layers = self.iface.legendInterface().selectedLayers()
        for layer in layers:
            if layer.type(
            ) == QgsMapLayer.VectorLayer and layer.selectedFeatureCount() > 0:
                crsDest = layer.crs()
                canvas = self.iface.mapCanvas()
                mapRenderer = canvas.mapSettings()
                crsSrc = mapRenderer.destinationCrs()
                crsTransformReverse = QgsCoordinateTransform(crsDest, crsSrc)

                if self.isLimitPreviewEnabled:
                    maxCount = self.maxLimitPreview
                else:
                    maxCount = layer.featureCount()

                currentCount = 0
                features = vectorlayerutils.features(layer)
                for f in features:
                    inGeom = f.geometry()
                    inGeom.transform(crsTransformReverse)
                    inGeom = self.moveGeometry(inGeom)
                    # in map coordinates
                    if inGeom.type() == QGis.Point:
                        self.rubberBandPointsPreview.addGeometry(inGeom, None)
                    else:
                        self.rubberBandGeometriesPreview.addGeometry(
                            inGeom, None)

                    currentCount += 1
                    if currentCount >= maxCount:
                        break

        self.rubberBandPointsPreview.show()
        self.rubberBandGeometriesPreview.show()

    def snap(self, pos):
        if self.digitizingTools.isBackgroundSnapping:
            (_, result) = self.snapper.snapToBackgroundLayers(pos)
        else:
            (_,
             result) = self.snapper.snapToCurrentLayer(pos,
                                                       QgsSnapper.SnapToVertex)
        return result
コード例 #10
0
class TrimDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())
        
        self.messageBarUtils = MessageBarUtils(iface)
        
        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())
        self.rubberBandCutting = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandCutting.setColor(QColor(255, 0, 0))
        self.rubberBandCutting.setWidth(2)
        
        self.layer = None
        self.reset()
    
    def setLayer(self, layer):
        self.layer = layer
        
    def reset(self, clearMessages = True):
        self.step = 0
        self.snappingResultCuttingEdge = None
        self.snappingResultTrim = None
        self.cachedSpatialIndex = None
        
        self.rubberBandCutting.reset(QGis.Line)
        
        try:self.segmentFinderTool.segmentFound.disconnect()
        except Exception: pass
        
        if clearMessages:
            self.messageBarUtils.removeAllMessages()
            
    def resetTrim(self):
        self.step = 1
        self.snappingResultTrim = None
        
    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)
        
    def next(self):
        if self.step == 0:
                        
            # alwways starts at this step: test if there is a selection
            if self.layer.selectedFeatureCount() > 0:
                # next
                self.step = 1
                self.next()
                return
            self.messageBarUtils.showButton("Trim", "Select cutting edge", "Use full layer", buttonCallback=self.useFullLayer)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.cuttingEdgeFound)
        elif self.step == 1:
            self.messageBarUtils.showButton("Trim", "Select segment to trim","Done",buttonCallback=self.done)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.trimEdgeFound)
        elif self.step == 2:
            self.doTrim()
         
    def cuttingEdgeFound(self, result, pointClicked):
        self.snappingResultCuttingEdge = result
        self.segmentFinderTool.segmentFound.disconnect(self.cuttingEdgeFound)
        self.segmentFinderTool.deactivate()
        
        self.rubberBandCutting.reset(QGis.Line)
        self.rubberBandCutting.addPoint(result.beforeVertex)
        self.rubberBandCutting.addPoint(result.afterVertex, True)
        self.rubberBandCutting.show()
        
        self.step = 1
        self.next()
        
    def trimEdgeFound(self, result, pointClicked):
        self.snappingResultTrim = result
        self.segmentFinderTool.segmentFound.disconnect(self.trimEdgeFound)
        self.segmentFinderTool.deactivate()
        
        transform = QgsCoordinateTransform(self.iface.mapCanvas().mapSettings().destinationCrs(), self.layer.crs())
        g = QgsGeometry.fromPoint(pointClicked)
        g.transform(transform)
        self.pointTrim = g.asPoint()
        
        self.step = 2
        self.next()
   
    def doTrim(self):
        self.messageBarUtils.showMessage("Trim", "Running...", duration=0)
        trimUtils = TrimUtils(self.iface)
        try:
            qDebug("New Trim")
            # 3 cases: selection on layer, whole layer, cutting edge
            if self.layer.selectedFeatureCount() > 0 or not self.snappingResultCuttingEdge:
                # use same strategy for both
                indexReferenceIntersection, pointIntersections  = self.getTrimmingInformationFromLayer(trimUtils)
            else:
                # no ambiguity here
                indexReferenceIntersection, pointIntersections = self.getTrimmingInformationFromSnappedGeometry(trimUtils)
                
            if indexReferenceIntersection != None:
                featureTrim = self._getFeature(self.snappingResultTrim)
                geometryTrim = featureTrim.geometry()
            
                parts, unconcernedParts = trimUtils.removeVertices(geometryTrim, indexReferenceIntersection, pointIntersections, 
                                                  self.snappingResultTrim.afterVertexNr, self.pointTrim)
                
                parts = self.cleanupTrimmedParts(parts)
                if len(parts) > 0:
                    if unconcernedParts:
                        parts.extend(unconcernedParts)
                    
                    # replace geometry
                    self.layer.beginEditCommand("Trim")
                    
                    # geometry updated => remove from index (readded very soon)
                    if self.cachedSpatialIndex:
                        self.cachedSpatialIndex.deleteFeature(featureTrim)
                    
                    # by default create a multipart geometry (unless not supported by data provider)
                    # for ex PostGIS layer can be constrained to be multipart
                    # some data types can have mixed multi/single (ex shp)
                    if (geometryTrim.isMultipart() or len(parts) > 1) and QGis.isMultiType(self.layer.dataProvider().geometryType()) :
                        qDebug("Multipart")
                        geometryTrim = QgsGeometry.fromMultiPolyline(map(lambda x:x.asPolyline(),parts))
                        self.layer.changeGeometry(featureTrim.id(), geometryTrim)
                        # for some reason, the line above makes it so the feature cannot be deleted from the index the next time
                        featureTrim.setGeometry(geometryTrim)
                        if self.cachedSpatialIndex:
                            self.cachedSpatialIndex.insertFeature(featureTrim)
                    else:
                        qDebug("Singlepart")
                        self.layer.deleteFeature(featureTrim.id())
                        # create a new feature for each part (so still single part geometries)
                        for part in parts:
                            featureTrim.setGeometry(part)
                            self.layer.addFeature(featureTrim)
                            if self.cachedSpatialIndex:
                                self.cachedSpatialIndex.insertFeature(featureTrim)
                        
                    self.layer.endEditCommand()
                    
                    self.iface.mapCanvas().refresh()
            
                    self.messageBarUtils.showMessage("Trim", "The segment was trimmed successfully", duration=2)
                else:
                    self.messageBarUtils.showMessage("Trim", "Nothing done: Trimming would create an invalid geometry", QgsMessageBar.WARNING, duration=2)
            else:
                self.messageBarUtils.showMessage("Trim", "Nothing done: The geometries are not intersecting", QgsMessageBar.WARNING, duration=2)
        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage("Trim", "There was an error performing this command. See QGIS Message log for details", QgsMessageBar.CRITICAL, duration=5)   
            self.layer.destroyEditCommand()
            self.done()
            
        self.resetTrim()
        self.next()
            
    def cleanupTrimmedParts(self, parts):
        return filter(lambda x: x and x.length() > constants.TOLERANCE_DEGREE*2, parts)
    
    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = self.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None 
    
    def _getSegment(self, snappingResult):
        feature = self._getFeature(snappingResult)
        geometry = feature.geometry()
        bv = geometry.vertexAt(snappingResult.beforeVertexNr)
        av = geometry.vertexAt(snappingResult.afterVertexNr)
        return QgsGeometry.fromPolyline([bv,av])
    
    def getTrimmingInformationFromSnappedGeometry(self, trimUtils):   
        edgeCuttingEdge = self._getSegment(self.snappingResultCuttingEdge)
        featureTrim = self._getFeature(self.snappingResultTrim)
        geometryTrim = featureTrim.geometry()
        
        bufferGeometryTrim = geometryTrim.buffer(constants.TOLERANCE_DEGREE, 2)
        
        if bufferGeometryTrim.intersects(edgeCuttingEdge):
            candidates = trimUtils.intersectionPointWithTolerance(edgeCuttingEdge, bufferGeometryTrim, geometryTrim)
            
        if len(candidates) > 0:
            index = self.indexOfClosestIntersection(candidates)
            return index, candidates
        return (None, None)
    
    def buildSpatialIndex(self):
        if not self.cachedSpatialIndex:
            # Build the spatial index for faster lookup.
            index = QgsSpatialIndex()
            for f in vectorlayerutils.features(self.layer):
                index.insertFeature(f)
            self.cachedSpatialIndex = index 
    
    def getTrimmingInformationFromLayer(self, trimUtils):
        try:
            self.buildSpatialIndex()
                
            # get geometry
            featureTrim = self._getFeature(self.snappingResultTrim)
            geometryTrim = featureTrim.geometry()
            
            # use buffered geometry to solve unexact geoemtries because of limited floating 
            # precision (point on a line never exactly on the line) 
            # (sometimes no intersection is present when there should be)
            bufferGeometryTrim = geometryTrim.buffer(constants.TOLERANCE_DEGREE, 2)
            
            fids = self.cachedSpatialIndex.intersects(bufferGeometryTrim.boundingBox())
            if  len(fids) > 0:
                candidates = []
                for fid in fids:
                    if fid == self.snappingResultTrim.snappedAtGeometry:
                        continue
                    features = self.layer.getFeatures(QgsFeatureRequest(fid))
                    feature = features.next()
                    geometryTest = feature.geometry()
                    if bufferGeometryTrim.intersects(geometryTest):
                        pointIntersection = trimUtils.intersectionPointWithTolerance(geometryTest, bufferGeometryTrim, geometryTrim)
                        candidates.extend(pointIntersection)
                        
                if len(candidates) > 0:
                    index = self.indexOfClosestIntersection(candidates)
                    return index, candidates
                
            return (None, None)
        except StopIteration:
            # index is invalid maybe because undo was performed: force rebuild
            # and relaunch
            self.cachedSpatialIndex = None
            return self.getTrimmingInformationFromLayer(trimUtils)
    
    def indexOfClosestIntersection(self, candidates):
        distancesToSnappedTrimPoint = map(lambda x: sqDistance(self.pointTrim, x), candidates)
        closestIndices = sorted(range(len(distancesToSnappedTrimPoint)),key=lambda i:distancesToSnappedTrimPoint[i])
        return closestIndices[0]
    
    def useFullLayer(self):
        self.snappingResultCuttingEdge = None
        # cleanup
        self.segmentFinderTool.segmentFound.disconnect(self.cuttingEdgeFound)
        self.segmentFinderTool.deactivate()
        
        self.step = 1
        self.next()
        
    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)
        
    def enter(self):
        #ignore
        pass
    
    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.useFullLayer()
                return
            elif self.step == 1:
                self.done()
                return
        
        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)
コード例 #11
0
class IntersectCommand(object):
    def __init__(self, iface):
        self.iface = iface
        self.messageBarUtils = MessageBarUtils(iface)

    def run(self):
        self.layer = self.iface.legendInterface().currentLayer()

        if self.layer.isModified():
            self.messageBarUtils.showMessage(
                "Intersect",
                "Commit current changes in layer before running this command.",
                QgsMessageBar.CRITICAL,
                duration=5)
            return

        if self.layer.selectedFeatureCount() == 0:
            self.messageBarUtils.showYesCancel(
                "Intersect",
                "No feature selected in Layer %s. Proceed on full layer?" %
                self.layer.name(), QgsMessageBar.WARNING, self.intersect)
            return

        # run directly without asking anything
        self.intersect()

    def intersect(self):
        self.layer.beginEditCommand("Intersect")

        tempDir = None
        try:
            self.iface.mapCanvas().freeze(True)

            if self.layer.selectedFeatureCount() > 0:
                selected = True
                concernedFids = self.layer.selectedFeaturesIds()
            else:
                concernedFids = self.layer.allFeatureIds()
                selected = False

            tempDir = tempfile.mkdtemp()
            shpPath = os.path.join(tempDir, "temp.shp")
            # not really necessary if no selection....
            QgsVectorFileWriter.writeAsVectorFormat(
                self.layer,
                shpPath,
                self.layer.dataProvider().encoding(),
                self.layer.crs(),
                onlySelected=selected)

            # here run own implementation of intersection instead of v:clean break => v.clean break applies a threshold
            # that merges vertices closer than 10^-4 to each other (no matter the unit), which is a problem for
            # goographic coordinates (10^-4 deg ~ 100m at equator). Possible to reproject instead but intersections not exactly where
            # they would have been (line in WGS84 is not a line in Mercator but reproject only reprojects the endpoint)
            # and someone would have complained (even though at the scale we work at; it would have been a distortion < 1mmm)
            # To consider if the method below is too slow...
            layer = QgsVectorLayer(shpPath, "Temp", "ogr")

            self.iface.mainWindow().statusBar().showMessage(
                "Layer Intersection")

            layer.startEditing()
            vectorlayerutils.layerIntersection(layer)
            layer.commitChanges()

            self.iface.mainWindow().statusBar().showMessage("v.clean snap")

            # v.clean Snap
            # will make sure the nodes snapped to a segment are taken into account
            # tolerance set to close to floating precision
            output = processing.runalg("grass:v.clean", layer, 1,
                                       constants.TOLERANCE_DEGREE, None, -1,
                                       0.0001, None, None)
            outputLayer = output['output']

            # here selection has been lost but not a problem in practice

            self.iface.mainWindow().statusBar().showMessage("v.clean rmdupl")

            # v.clean rmdupl
            output = processing.runalg("grass:v.clean", outputLayer, 6, 0,
                                       None, -1, 0.0001, None, None)
            outputLayer = output['output']

            self.iface.mainWindow().statusBar().showMessage("v.clean rmline")

            # v.clean rmline
            output = processing.runalg("grass:v.clean", outputLayer, 11, 0,
                                       None, -1, 0.0001, None, None)
            outputLayer = output['output']

            outputLayer = QgsVectorLayer(outputLayer, "Repr", "ogr")

            self.iface.mainWindow().statusBar().showMessage("output")

            # copy output features
            count = outputLayer.featureCount()
            qDebug("Count %d" % count)
            if count == 0:
                self.messageBarUtils.showMessage("Intersect",
                                                 "No feature output.",
                                                 QgsMessageBar.WARNING,
                                                 duration=5)
            else:
                # delete original selected features
                for featureId in concernedFids:
                    self.layer.deleteFeature(featureId)

                for feature in outputLayer.getFeatures():
                    geom = feature.geometry()
                    feature.setGeometry(geom)
                    self.layer.addFeature(feature)
                    if selected:
                        self.layer.select(feature.id())

            del outputLayer

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.removeAllMessages()
            self.messageBarUtils.showMessage(
                "Intersect",
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)

            self.layer.destroyEditCommand()

            return
        finally:
            if tempDir:
                shutil.rmtree(tempDir, True)
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()

        self.messageBarUtils.showMessage("Intersect", "Success", duration=5)

        self.layer.endEditCommand()
コード例 #12
0
class SplitDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0

        self.rubberBandSnap.reset(QGis.Point)

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showMessage("Split",
                                             "Select segment to cut",
                                             duration=0)
        elif self.step == 1:
            self.doSplit()

    def doSplit(self):
        self.messageBarUtils.showMessage("Split", "Running...", duration=0)
        try:
            qDebug("New Split")
            self.layer.beginEditCommand("Split")

            featureToSplit = self._getFeature(self.snappingResultSplit)
            segmentSplit = self._getSegment(self.snappingResultSplit)
            vertexSplit = self.snappingResultSplit.snappedVertex

            # transform snapped vertex
            vertexSplit = self.iface.mapCanvas().mapSettings(
            ).mapToLayerCoordinates(self.layer, vertexSplit)
            _, _, vertexSplit = vectorlayerutils.closestSegmentWithContext(
                vertexSplit, segmentSplit.asPolyline())

            newGeometries = self.splitAt(
                featureToSplit.geometry(), vertexSplit,
                self.snappingResultSplit.snappedVertexNr)

            if len(newGeometries) > 1:
                # change the first one
                self.layer.changeGeometry(featureToSplit.id(),
                                          newGeometries[0])

                # create new ones for every other geometry
                attrs = featureToSplit.attributes()
                pgidIndex = self.layer.fieldNameIndex("gid")
                newFeature = QgsFeature()
                for indexGeometry in range(1, len(newGeometries)):
                    newFeature.initAttributes(len(attrs))
                    for index in range(len(attrs)):
                        if index <> pgidIndex:
                            newFeature.setAttribute(index, attrs[index])
                    newFeature.setGeometry(newGeometries[indexGeometry])
                    self.layer.addFeature(newFeature)

                self.messageBarUtils.showMessage(
                    "Split", "The segment was split successfully", duration=2)
            else:
                self.messageBarUtils.showMessage("Split",
                                                 "The segment was not split",
                                                 QgsMessageBar.WARNING,
                                                 duration=2)

            self.layer.endEditCommand()

            self.iface.mapCanvas().refresh()

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Split",
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.layer.destroyEditCommand()
            raise
        finally:
            self.iface.mapCanvas().unsetMapTool(self)

    def splitAt(self, geometry, vertex, vertexNr):
        if vertexNr == -1:
            geometry.insertVertex(vertex.x(), vertex.y(),
                                  self.snappingResultSplit.afterVertexNr)
            splitIndex = self.snappingResultSplit.afterVertexNr
        else:
            # no need to add a new vertex
            splitIndex = vertexNr

        if geometry.isMultipart():
            vertexCount = 0
            mp = geometry.asMultiPolyline()
            for index in range(len(mp)):
                p = mp[index]
                if vertexCount + len(p) > splitIndex:
                    relativeSplitIndex = splitIndex - vertexCount
                    splitLine1 = p[0:relativeSplitIndex + 1]
                    splitLine2 = p[relativeSplitIndex:]

                    firstGeometry = mp[0:index]
                    firstGeometry.append(splitLine1)
                    secondGeometry = [splitLine2]
                    secondGeometry.extend(mp[index + 1:])

                    qDebug(repr(firstGeometry))

                    return (QgsGeometry.fromMultiPolyline(firstGeometry),
                            QgsGeometry.fromMultiPolyline(secondGeometry))
                else:
                    vertexCount += len(p)
        else:
            p = geometry.asPolyline()
            # a vertex has been added so splitVertex has index afterVertexNr
            return (QgsGeometry.fromPolyline(p[0:splitIndex + 1]),
                    QgsGeometry.fromPolyline(p[splitIndex:]))

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = self.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def _getSegment(self, snappingResult):
        feature = self._getFeature(snappingResult)
        geometry = feature.geometry()
        bv = geometry.vertexAt(snappingResult.beforeVertexNr)
        av = geometry.vertexAt(snappingResult.afterVertexNr)
        return QgsGeometry.fromPolyline([bv, av])

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        pass

    def canvasReleaseEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        if result != []:
            self.snappingResultSplit = result[0]
            self.step = 1
            self.next()

    def canvasMoveEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        self.rubberBandSnap.reset(QGis.Point)
        if result != []:
            vertex = result[0].snappedVertex
            if result[0].snappedVertexNr == -1:
                segment = self._getSegment(result[0])
                vertex = self.iface.mapCanvas().mapSettings(
                ).mapToLayerCoordinates(self.layer, vertex)
                _, _, vertex = vectorlayerutils.closestSegmentWithContext(
                    vertex, segment.asPolyline())
                vertex = self.iface.mapCanvas().mapSettings(
                ).layerToMapCoordinates(self.layer, vertex)
            self.rubberBandSnap.addPoint(vertex, True)

    def snap(self, pos):
        (_, result) = self.snapper.snapToCurrentLayer(
            pos, QgsSnapper.SnapToVertexAndSegment)
        return result
コード例 #13
0
ファイル: scale.py プロジェクト: gvellut/GVDigitizingTools
class ScaleDigitizingMode(QgsMapToolEmitPoint):

    MESSAGE_HEADER = "Scale"
    DEFAULT_SCALE_FACTOR = 2.0

    def __init__(self, iface, digitizingTools):
        self.iface = iface
        self.digitizingTools = digitizingTools
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        # we need a snapper, so we use the MapCanvas snapper
        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0

        self.rubberBandSnap.reset(QGis.Point)

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def deactivate(self):
        self.reset()
        super(QgsMapToolEmitPoint, self).deactivate()

    def next(self):
        if self.step == 0:
            scaleFactor, found = QgsProject.instance().readEntry(
                constants.SETTINGS_KEY, constants.SETTINGS_SCALE_FACTOR, None)
            if not found:
                scaleFactor = str(ScaleDigitizingMode.DEFAULT_SCALE_FACTOR)

            _, lineEdit = self.messageBarUtils.showLineEdit(
                ScaleDigitizingMode.MESSAGE_HEADER,
                "Draw base point and set scale:", scaleFactor)
            self.lineEditScaleFactor = lineEdit
        elif self.step == 1:
            self.scaleFactor = self.lineEditScaleFactor.text()

            if self.layer.selectedFeatureCount() == 0:
                self.messageBarUtils.showYesCancel(
                    ScaleDigitizingMode.MESSAGE_HEADER,
                    "No feature selected in Layer %s. Proceed on full layer?" %
                    self.layer.name(), QgsMessageBar.WARNING, self.doScale)
                return

            self.doScale()

    def doScale(self):
        _, progress = self.messageBarUtils.showProgress(
            ScaleDigitizingMode.MESSAGE_HEADER, "Running...",
            QgsMessageBar.INFO)

        try:
            # get the actual vertices from the geometry instead of the snapping
            crsDest = self.layer.crs()
            canvas = self.iface.mapCanvas()
            mapRenderer = canvas.mapSettings()
            crsSrc = mapRenderer.destinationCrs()
            crsTransform = QgsCoordinateTransform(crsSrc, crsDest)
            # arcnode is still in map canvas coordinate => transform to layer coordinate
            self.basePoint = crsTransform.transform(self.basePoint)

            try:
                scaleFactor = float(self.scaleFactor)
                # save for next time
                QgsProject.instance().writeEntry(
                    constants.SETTINGS_KEY, constants.SETTINGS_SCALE_FACTOR,
                    scaleFactor)
            except ValueError:
                scaleFactor = ScaleDigitizingMode.DEFAULT_SCALE_FACTOR

            self.iface.mapCanvas().freeze(True)

            outFeat = QgsFeature()
            inGeom = QgsGeometry()

            current = 0
            features = vectorlayerutils.features(self.layer)
            total = 100.0 / float(len(features))
            for f in features:
                inGeom = f.geometry()
                attrs = f.attributes()

                # break into multiple geometries if needed
                if inGeom.isMultipart():
                    geometries = inGeom.asMultiPolyline()
                    outGeom = []

                    for g in geometries:
                        polyline = self.scalePolyline(g, scaleFactor)
                        outGeom.append(polyline)

                    outGeom = QgsGeometry.fromMultiPolyline(outGeom)

                else:
                    polyline = self.scalePolyline(inGeom.asPolyline(),
                                                  scaleFactor)
                    outGeom = QgsGeometry.fromPolyline(polyline)

                outFeat.setAttributes(attrs)
                outFeat.setGeometry(outGeom)
                self.layer.addFeature(outFeat)

                self.layer.deleteFeature(f.id())

                current += 1
                progress.setValue(int(current * total))

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                ScaleDigitizingMode.MESSAGE_HEADER,
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.layer.destroyEditCommand()

            return
        finally:
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()

        # if here: Success!
        self.messageBarUtils.showMessage(ScaleDigitizingMode.MESSAGE_HEADER,
                                         "Success", QgsMessageBar.INFO, 5)

        self.layer.endEditCommand()

        self.iface.mapCanvas().unsetMapTool(self)

    def scalePolyline(self, polyline, scaleFactor):
        for vertex in polyline:
            # scale
            relBP = QgsPoint(vertex.x() - self.basePoint.x(),
                             vertex.y() - self.basePoint.y())
            vertex.setX(self.basePoint.x() + scaleFactor * relBP.x())
            vertex.setY(self.basePoint.y() + scaleFactor * relBP.y())

        return polyline

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        pass

    def canvasReleaseEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        if result != []:
            self.basePoint = result[0].snappedVertex
        else:
            self.basePoint = self.toMapCoordinates(e.pos())

        self.step = 1
        self.next()

    def canvasMoveEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        self.rubberBandSnap.reset(QGis.Point)
        if result != []:
            self.rubberBandSnap.addPoint(result[0].snappedVertex, True)

    def snap(self, pos):
        if self.digitizingTools.isBackgroundSnapping:
            (_, result) = self.snapper.snapToBackgroundLayers(pos)
        else:
            (_,
             result) = self.snapper.snapToCurrentLayer(pos,
                                                       QgsSnapper.SnapToVertex)
        return result
コード例 #14
0
class PolygonizeCommand(object):
         
    def __init__(self, iface):
        self.iface = iface
        self.messageBarUtils = MessageBarUtils(iface)
        
    def run(self): 
        self.layer = self.iface.legendInterface().currentLayer()
        
        if self.layer.isModified():
            self.messageBarUtils.showMessage("Polygonize", "Commit current changes in layer before running this command.", 
                                               QgsMessageBar.CRITICAL, duration=5)
            return
        
        if self.layer.featureCount() == 0:
            self.messageBarUtils.showMessage("Polygonize", "Current layer is empty!", 
                                               QgsMessageBar.WARNING, duration=5)
            return
        
        self.polygonLayers, self.nonDigitLayerIndex = self.getPolygonLayerList()
        if len(self.polygonLayers) == 0:
            self.messageBarUtils.showMessage("Polygonize", "No polygon layer in TOC.", 
                                               QgsMessageBar.WARNING, duration=5)
            return
        
        if self.layer.selectedFeatureCount() == 0:
            self.messageBarUtils.showYesCancel("Polygonize", "No feature selected in Layer %s. Proceed on full layer?" % self.layer.name(), 
                                               QgsMessageBar.WARNING, self.showDialog)
            return
        
        self.showDialog()
        
    def showDialog(self):
        dialog = PolygonizeDialog(self.polygonLayers, self.nonDigitLayerIndex)
        dialog.show()
        result = dialog.exec_()
        if result == 1:
            self.polygonize(dialog.selectedLayer, dialog.append)
        
    def getPolygonLayerList(self):        
        polygonLayers = []
        legend = self.iface.legendInterface()
        nonDigitLayerName = None
        if "_DIGIT" in self.layer.name():
            # output in the non digit polygon layer
            nonDigitLayerName = self.layer.name().replace("_DIGIT", "")
        
        nonDigitLayerIndex = None
        for layer in legend.layers():
            if layer.type() == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Polygon:
                polygonLayers.append(layer)
                if nonDigitLayerName == layer.name():
                    nonDigitLayerIndex = len(polygonLayers) - 1
                    
        return polygonLayers, nonDigitLayerIndex
    
    def polygonize(self, outputLayer, append):
        try:
            from shapely.ops import polygonize
            from shapely.geometry import Point, MultiLineString
        except ImportError:
            self.messageBarUtils.showMessage("Polygonize", "Polygonize requires the Shapely Python module. Please contact your administrator.", QgsMessageBar.CRITICAL, 5)
            return
        
        self.outputLayer = outputLayer
        self.append = append
        
        outputEditable = False
        if self.outputLayer.isEditable():
            outputEditable = True
            self.outputLayer.beginEditCommand("Polygonize")
        else:
            self.outputLayer.startEditing()    
        
        grassOutputLayer=None
        tempDir = None
        try:
            
            selected = self.layer.selectedFeatureCount() > 0
            
            tempDir = tempfile.mkdtemp()
            shpPath = os.path.join(tempDir, "temp.shp")
            QgsVectorFileWriter.writeAsVectorFormat(self.layer, shpPath, self.layer.dataProvider().encoding(), 
                                                    self.layer.crs(), onlySelected=selected)
            grassOutputLayer = self.cleanupGRASS(shpPath)
            
            
            self.iface.mainWindow().statusBar().showMessage("Cleanup duplicate and degenerate lines")
            allLinesList = self.cleanupDuplicateAndDegenerateLines(grassOutputLayer)
            
            _, progress = self.messageBarUtils.showProgress("Polygonize", "Running...", QgsMessageBar.INFO)
                
            
            self.iface.mainWindow().statusBar().showMessage("Shapely polygonize")
                
            progress.setValue(10)
            allLines = MultiLineString(allLinesList)
            allLines = allLines.union(Point(0, 0))
            progress.setValue(25)
            polygons = list(polygonize([allLines]))
            progress.setValue(35)
            
            outFeat = QgsFeature()
            
            outputFields = self.outputLayer.dataProvider().fields()
            
            
            self.iface.mainWindow().statusBar().showMessage("Freeze canvas before update")
            
            self.iface.mapCanvas().freeze(True)
            
            if not append:
                # delete all features in outputlayer
                for feature in self.outputLayer.getFeatures():
                    self.outputLayer.deleteFeature(feature.id())
            
            progress.setValue(50)
            
            
            self.iface.mainWindow().statusBar().showMessage("add to output")
            
            current = 0
            if len(polygons) > 0:
                total = 50.0 / float(len(polygons))
                for polygon in polygons:
                    geom = QgsGeometry.fromWkt(polygon.wkt)
                    outFeat.setGeometry(geom)
                    outFeat.initAttributes(outputFields.count())
                    self.outputLayer.addFeature(outFeat)
                    current += 1
                    progress.setValue(50 + int(current * total))
                
                self.messageBarUtils.showMessage("Polygonize", "Success: %d polygons created in %s" %(current, self.outputLayer.name()), QgsMessageBar.INFO, 2)
            else:
                self.messageBarUtils.showMessage("Polygonize", "No polygon was created", QgsMessageBar.WARNING, 2)
                
        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.removeAllMessages()
            self.messageBarUtils.showMessage("Polygonize", "There was an error performing this command. See QGIS Message log for details.", QgsMessageBar.CRITICAL, duration=5)
            
            if outputEditable:
                self.outputLayer.destroyEditCommand()
            else:
                self.outputLayer.rollBack()
            return
        finally:
            if tempDir:
                shutil.rmtree(tempDir, True)
            if grassOutputLayer:
                del grassOutputLayer
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()
        
        if outputEditable:
            self.outputLayer.endEditCommand()
        else:
            self.outputLayer.commitChanges()
            
    def cleanupGRASS(self, layer):
        
        layer = QgsVectorLayer(layer, "Temp", "ogr")
    
        self.iface.mainWindow().statusBar().showMessage("GRASS cleanup: Layer Intersection")
    
        layer.startEditing()
        vectorlayerutils.layerIntersection(layer)
        layer.commitChanges() 
        
        self.iface.mainWindow().statusBar().showMessage("GRASS cleanup: v.clean Snap")
        
        # v.clean Snap
        # will make sure the nodes snapped to a segment are taken into account during v.clean break
        # tolerance in m => 1mm
        output = processing.runalg("grass:v.clean",layer,1,constants.TOLERANCE_DEGREE,None,-1,0.0001,None,None)
        outputLayer = output['output']
        del layer
        
        
        self.iface.mainWindow().statusBar().showMessage("GRASS cleanup: v.clean rmdupl")
        
        # v.clean rmdupl
        output = processing.runalg("grass:v.clean",outputLayer,6,0,None,-1,0.0001,None,None)
        if output and os.path.exists(output['output']):
            outputLayer = output['output']
            
        
        self.iface.mainWindow().statusBar().showMessage("GRASS cleanup: v.clean rmline")
        
        # v.clean rmline
        output = processing.runalg("grass:v.clean",outputLayer,11,0,None,-1,0.0001,None,None)
        if output and os.path.exists(output['output']):
            outputLayer = output['output']
            
        
        self.iface.mainWindow().statusBar().showMessage("GRASS cleanup: Open layer")
        
        outputLayer = QgsVectorLayer(outputLayer, "Repr", "ogr")
        
        return outputLayer   
        
    def cleanupDuplicateAndDegenerateLines(self, layer):
        allLinesList = []
        wkts = set()
        features = layer.getFeatures()
        for inFeat in features:
            inGeom = inFeat.geometry()
            if inGeom.isMultipart():
                for polyline in inGeom.asMultiPolyline():
                    if self.checkPolyline(self.cutAtVertices(polyline), wkts):
                        allLinesList.extend(polyline)
                    else:
                        QgsMessageLog.logMessage("Bad polyline: " + str(inFeat.id()))
            else:
                polyline = inGeom.asPolyline()
                if self.checkPolyline(polyline, wkts):
                    allLinesList.extend(self.cutAtVertices(polyline))
                else:
                    QgsMessageLog.logMessage("Bad polyline: " + str(inFeat.id()))
            
        return allLinesList
        
    def cutAtVertices(self,polyline):
        cut=[]
        for i in range(1,len(polyline)):
            cut.append(polyline[i-1:i+1])
        return cut
        
    def checkPolyline(self, polyline, wkts):
        # check degenerate
        if self.checkDegeneratePolyline(polyline):
            wkt = QgsGeometry.fromPolyline(polyline).exportToWkt()
            # check duplicate
            if wkt in wkts:
                return False
            else:
                wkts.add(wkt)
                return True
        else:
            return False
            
    def checkDegeneratePolyline(self, polyline):
        return len(polyline) >= 2
コード例 #15
0
ファイル: arc.py プロジェクト: gvellut/GVDigitizingTools
class DrawArcDigitizingMode(QgsMapToolEmitPoint):

    MESSAGE_HEADER = "Draw Arc"

    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())

        self.rubberBandBase = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandBase.setColor(QColor(255, 0, 0))
        self.rubberBandBase.setWidth(2)

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0
        self.baseEdge = None

        self.rubberBandBase.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def deactivate(self):
        self.reset()
        super(QgsMapToolEmitPoint, self).deactivate()

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showMessage(
                DrawArcDigitizingMode.MESSAGE_HEADER,
                "Select base edge",
                duration=0)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.baseEdgeFound)
        elif self.step == 1:
            self.messageBarUtils.showMessage(
                DrawArcDigitizingMode.MESSAGE_HEADER,
                "Draw node for arc",
                duration=0)
            self.currentMapTool = None
        elif self.step == 2:
            self.doDrawArc()

    def baseEdgeFound(self, result, pointClicked):
        self.baseEdge = result
        self.segmentFinderTool.segmentFound.disconnect(self.baseEdgeFound)
        self.segmentFinderTool.deactivate()

        self.rubberBandBase.reset(QGis.Line)
        self.rubberBandBase.addPoint(result.beforeVertex)
        self.rubberBandBase.addPoint(result.afterVertex, True)
        self.rubberBandBase.show()

        self.step = 1
        self.next()

    def doDrawArc(self):
        self.messageBarUtils.showMessage(DrawArcDigitizingMode.MESSAGE_HEADER,
                                         "Running...",
                                         duration=0)
        try:
            # get the actual vertices from the geometry instead of the snapping
            # result vertices which are in map canvas CRS
            featureBase = self._getFeature(self.baseEdge)
            geometryBase = featureBase.geometry()

            settings = QSettings()
            method = settings.value(constants.ARC_METHOD, "")
            if method == constants.ARC_METHOD_NUMBEROFPOINTS:
                value = settings.value(constants.ARC_NUMBEROFPOINTS,
                                       0,
                                       type=int)
            else:
                value = settings.value(constants.ARC_ANGLE, 0, type=float)

            # arc done in map canvas coordinate
            g = CircularArc.getInterpolatedArc(self.baseEdge.beforeVertex,
                                               self.arcNode,
                                               self.baseEdge.afterVertex,
                                               method, value)
            arcVertices = g.asPolyline()
            if len(arcVertices) > 2:
                # to project the vertices of the arc in layer crs
                crsDest = self.layer.crs()
                canvas = self.iface.mapCanvas()
                mapRenderer = canvas.mapSettings()
                crsSrc = mapRenderer.destinationCrs()
                crsTransform = QgsCoordinateTransform(crsSrc, crsDest)

                # pass over first and last vertex (bvBase and avBase)
                for i in range(len(arcVertices) - 2, 0, -1):
                    arcVertex = arcVertices[i]
                    arcVertex = crsTransform.transform(arcVertex)
                    geometryBase.insertVertex(arcVertex.x(), arcVertex.y(),
                                              self.baseEdge.afterVertexNr)
                    # the newly inserted vertex has the index of the original avBase so all good

                self.layer.beginEditCommand(
                    DrawArcDigitizingMode.MESSAGE_HEADER)
                self.layer.changeGeometry(featureBase.id(), geometryBase)
                self.layer.endEditCommand()

                self.messageBarUtils.showMessage(
                    DrawArcDigitizingMode.MESSAGE_HEADER,
                    "Success",
                    QgsMessageBar.INFO,
                    duration=5)
            else:
                self.messageBarUtils.showMessage(
                    DrawArcDigitizingMode.MESSAGE_HEADER,
                    "No arc was created",
                    QgsMessageBar.WARNING,
                    duration=5)

        except Exception as e:
            self.reset()
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                DrawArcDigitizingMode.MESSAGE_HEADER,
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)

        finally:
            self.iface.mapCanvas().refresh()

        self.iface.mapCanvas().unsetMapTool(self)

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = self.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)
        elif self.step == 1:
            # just point selection
            self.arcNode = self.toMapCoordinates(e.pos())
            self.step = 2
            self.next()

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)
コード例 #16
0
class ExtendDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())
        self.rubberBandBoundary = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandBoundary.setColor(QColor(255, 0, 0))
        self.rubberBandBoundary.setWidth(2)

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0
        self.snappingResultBoundaryEdge = None
        self.snappingResultExtendEdge = None
        self.cachedSpatialIndex = None

        self.rubberBandBoundary.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def resetExtend(self, full=False):
        if full:
            self.reset(False)
        else:
            # jsut the last step
            self.step = 1
            self.snappingResultExtendEdge = None

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            # always starts at this step: test if there is a selection
            if self.layer.selectedFeatureCount() > 0:
                # next
                self.step = 1
                self.next()
                return
            self.messageBarUtils.showButton("Extend",
                                            "Select boundary edge",
                                            "Use full layer",
                                            buttonCallback=self.useFullLayer)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.boundaryEdgeFound)
        elif self.step == 1:
            self.messageBarUtils.showButton("Extend",
                                            "Select edge to extend",
                                            "Done",
                                            buttonCallback=self.done)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.extendEdgeFound)
        elif self.step == 2:
            self.doExtend()

    def boundaryEdgeFound(self, result, pointClicked):
        self.snappingResultBoundaryEdge = result
        self.segmentFinderTool.segmentFound.disconnect(self.boundaryEdgeFound)
        self.segmentFinderTool.deactivate()

        self.rubberBandBoundary.reset(QGis.Line)
        self.rubberBandBoundary.addPoint(result.beforeVertex)
        self.rubberBandBoundary.addPoint(result.afterVertex, True)
        self.rubberBandBoundary.show()

        self.step = 1
        self.next()

    def extendEdgeFound(self, result, pointClicked):
        self.snappingResultExtendEdge = result
        self.segmentFinderTool.segmentFound.disconnect(self.extendEdgeFound)
        self.segmentFinderTool.deactivate()

        transform = QgsCoordinateTransform(
            self.iface.mapCanvas().mapSettings().destinationCrs(),
            self.layer.crs())
        g = QgsGeometry.fromPoint(pointClicked)
        g.transform(transform)
        self.pointExtend = g.asPoint()

        self.step = 2
        self.next()

    def doExtend(self):
        self.messageBarUtils.showMessage("Extend", "Running...", duration=0)
        extendUtils = ExtendUtils(self.iface)
        try:
            featureExtend = self._getFeature(self.snappingResultExtendEdge)
            geometryExtend = featureExtend.geometry()
            bvExtend = geometryExtend.vertexAt(
                self.snappingResultExtendEdge.beforeVertexNr)
            avExtend = geometryExtend.vertexAt(
                self.snappingResultExtendEdge.afterVertexNr)

            vertexNrToMove = extendUtils.vertexIndexToMove(
                bvExtend,
                avExtend,
                self.snappingResultExtendEdge,
                self.pointExtend,
                mustExtend=False)
            vertexToMove = geometryExtend.vertexAt(vertexNrToMove)

            testRay = self.getTestRayForExtend(extendUtils, geometryExtend,
                                               bvExtend, avExtend,
                                               vertexNrToMove)
            # 3 cases: selection on layer, whole layer, cutting edge
            if self.layer.selectedFeatureCount(
            ) > 0 or not self.snappingResultBoundaryEdge:
                # use same strategy for both
                pointIntersection, alreadyNearEdge = self.getExtendInformationFromLayer(
                    extendUtils, testRay, vertexToMove)
            else:
                # no ambiguity here
                pointIntersection, alreadyNearEdge = self.getExtendInformationFromSnappedBoundaryEdge(
                    extendUtils, testRay, vertexToMove)

            if alreadyNearEdge:
                self.messageBarUtils.showMessage(
                    "Extend",
                    "Nothing done: Already at boundary edge",
                    QgsMessageBar.WARNING,
                    duration=2)
            elif pointIntersection:
                if self.cachedSpatialIndex:
                    self.cachedSpatialIndex.deleteFeature(featureExtend)

                fid = featureExtend.id()
                geometryExtend.moveVertex(pointIntersection.x(),
                                          pointIntersection.y(),
                                          vertexNrToMove)
                self.layer.beginEditCommand("Extend")
                self.layer.changeGeometry(fid, geometryExtend)
                if self.cachedSpatialIndex:
                    self.cachedSpatialIndex.insertFeature(featureExtend)
                self.layer.endEditCommand()

                self.iface.mapCanvas().refresh()

                self.messageBarUtils.showMessage(
                    "Extend",
                    "The segment was extended successfully",
                    duration=2)
            else:
                self.messageBarUtils.showMessage(
                    "Extend",
                    "Nothing done: No suitable boundary edge",
                    QgsMessageBar.WARNING,
                    duration=2)
        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Trim",
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)

        # select another edge to extend
        self.resetExtend()
        self.next()

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = self.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def buildSpatialIndex(self):
        if not self.cachedSpatialIndex:
            # Build the spatial index for faster lookup.
            index = QgsSpatialIndex()
            for f in vectorlayerutils.features(self.layer):
                index.insertFeature(f)
            self.cachedSpatialIndex = index

    def isNearEdgeInLayer(self, point, distance):
        bufferGeometry = QgsGeometry.fromPoint(point).buffer(distance, 2)
        fids = self.cachedSpatialIndex.intersects(bufferGeometry.boundingBox())
        for fid in fids:
            if fid == self.snappingResultExtendEdge.snappedAtGeometry:
                continue
            feature = self.layer.getFeatures(QgsFeatureRequest(fid)).next()
            geometryTest = feature.geometry()
            if bufferGeometry.intersects(geometryTest):
                return True
        return False

    def getExtendInformationFromLayer(self, extendUtils, testRay,
                                      vertexToMove):
        self.buildSpatialIndex()

        if self.isNearEdgeInLayer(vertexToMove, constants.TOLERANCE_DEGREE):
            return (None, True)

        # get segment
        fids = self.cachedSpatialIndex.intersects(testRay.boundingBox())
        candidates = []
        for fid in fids:
            if fid == self.snappingResultExtendEdge.snappedAtGeometry:
                continue
            feature = self.layer.getFeatures(QgsFeatureRequest(fid)).next()
            geometryTest = feature.geometry()
            if testRay.intersects(geometryTest):
                candidates.extend(
                    vectorlayerutils.segmentIntersectionPoint(
                        geometryTest, testRay))

        # extend as short as possible among the candidate intersection points
        if len(candidates) > 0:
            distancesToVertexToMove = map(
                lambda x: self.sqDistance(vertexToMove, x), candidates)
            closestIndices = sorted(range(len(distancesToVertexToMove)),
                                    key=lambda i: distancesToVertexToMove[i])
            return (candidates[closestIndices[0]], False)

        return (None, False)

    def isNearSingleEdge(self, point, edge, distance):
        bufferGeometry = QgsGeometry.fromPoint(point).buffer(distance, 2)
        return edge.intersects(bufferGeometry)

    def getExtendInformationFromSnappedBoundaryEdge(self, extendUtils, testRay,
                                                    vertexToMove):
        featureBoundary = self._getFeature(self.snappingResultBoundaryEdge)
        geometryBoundary = featureBoundary.geometry()
        bvBoundary = geometryBoundary.vertexAt(
            self.snappingResultBoundaryEdge.beforeVertexNr)
        avBoundary = geometryBoundary.vertexAt(
            self.snappingResultBoundaryEdge.afterVertexNr)
        edgeBoundary = QgsGeometry.fromPolyline([bvBoundary, avBoundary])

        if self.isNearSingleEdge(vertexToMove, edgeBoundary,
                                 constants.TOLERANCE_DEGREE):
            return (None, True)

        if testRay.intersects(edgeBoundary):
            # only one possible intersection point (2 edges)
            pointIntersection = vectorlayerutils.segmentIntersectionPoint(
                testRay, edgeBoundary)
            if pointIntersection:
                pointIntersection = pointIntersection[0]
            return (pointIntersection, False)

        return (None, False)

    def getTestRayForExtend(self, extendUtils, geometryExtend, bvExtend,
                            avExtend, vertexNrToMove):
        # Create a segment that goes away from the edgeExtend, starting at vertexNrToMove
        # and ending at the bounding box of the layer
        if vertexNrToMove == self.snappingResultExtendEdge.afterVertexNr:
            baseVertex = avExtend
            # direction towards which the extension will be performed
            vectorDirection = [bvExtend, avExtend]
        else:
            baseVertex = bvExtend
            vectorDirection = [avExtend, bvExtend]

        boundingBox = self.layer.extent()
        topLeft = QgsPoint(boundingBox.xMinimum(), boundingBox.yMaximum())
        topRight = QgsPoint(boundingBox.xMaximum(), boundingBox.yMaximum())
        bottomRight = QgsPoint(boundingBox.xMaximum(), boundingBox.yMinimum())
        bottomLeft = QgsPoint(boundingBox.xMinimum(), boundingBox.yMinimum())

        topIntersection = extendUtils.intersectionPoint(
            bvExtend, avExtend, topLeft, topRight)
        # check that the vector from baseVertex to that point is in the same direction as
        # vector direction
        if topIntersection and self.dp(vectorDirection[0], vectorDirection[1],
                                       baseVertex, topIntersection) > 0:
            return QgsGeometry.fromPolyline([baseVertex, topIntersection])

        rightIntersection = extendUtils.intersectionPoint(
            bvExtend, avExtend, topRight, bottomRight)
        if rightIntersection and self.dp(vectorDirection[0],
                                         vectorDirection[1], baseVertex,
                                         rightIntersection) > 0:
            return QgsGeometry.fromPolyline([baseVertex, rightIntersection])

        bottomIntersection = extendUtils.intersectionPoint(
            bvExtend, avExtend, bottomRight, bottomLeft)
        if bottomIntersection and self.dp(vectorDirection[0],
                                          vectorDirection[1], baseVertex,
                                          bottomIntersection) > 0:
            return QgsGeometry.fromPolyline([baseVertex, bottomIntersection])

        leftIntersection = extendUtils.intersectionPoint(
            bvExtend, avExtend, bottomLeft, topLeft)
        if leftIntersection and self.dp(vectorDirection[0], vectorDirection[1],
                                        baseVertex, leftIntersection) > 0:
            return QgsGeometry.fromPolyline([baseVertex, leftIntersection])

    def dp(self, pt11, pt12, pt21, pt22):
        return (pt12.x() - pt11.x()) * (pt22.x() - pt21.x()) + (
            pt12.y() - pt11.y()) * (pt22.y() - pt21.y())

    def sqDistance(self, pt1, pt2):
        return (pt1.x() - pt2.x())**2 + (pt1.y() - pt2.y())**2

    def useFullLayer(self):
        self.snappingResultBoundaryEdge = None
        self.segmentFinderTool.segmentFound.disconnect(self.boundaryEdgeFound)
        self.segmentFinderTool.deactivate()

        self.step = 1
        self.next()

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.useFullLayer()
                return
            elif self.step == 1:
                self.resetExtend(True)
                self.next()
                return

        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)
コード例 #17
0
class CenterlineAdvancedDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)
        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())

        # just needs to rubberbands (when selecting 2nd segment, replaced by selectedLine or
        # hidden)
        self.rubberBandSelectedSegment = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedSegment.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedSegment.setWidth(2)

        self.rubberBandSelectedLine = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedLine.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedLine.setWidth(2)

        self.defaultIndex = None
        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0
        self.snappingResults = []
        self.snappingPointsClicked = []
        self.subPolylines = []
        self.defaultIndex = None
        self.orderSelection = False

        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.rubberBandSelectedLine.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def resetCenterline(self):
        self.step = 0
        self.snappingResults = []
        self.snappingPointsClicked = []
        self.subPolylines = []
        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.rubberBandSelectedLine.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showButton(
                "Centerline",
                "Select starting segment of first line",
                "Done",
                buttonCallback=self.done)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.edgeFound)
        elif self.step == 1:
            self.messageBarUtils.showMessage(
                "Centerline", "Select end segment of first line", duration=0)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.edgeFound)
        elif self.step == 2:
            self.messageBarUtils.showMessage(
                "Centerline",
                "Select starting segment of second line",
                duration=0)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.edgeFound)
        elif self.step == 3:
            candidates, candidateLayers, defaultIndex = self.listCandidateLayers(
                onlyEditable=False)
            if self.defaultIndex <> None and defaultIndex < len(
                    candidateLayers):
                defaultIndex = self.defaultIndex
            _, combobox = self.messageBarUtils.showCombobox(
                "Centerline", "Select end segment of second line", candidates,
                defaultIndex)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(
                partial(self.lastEdgeFound, candidateLayers, combobox))
        elif self.step == 4:
            self.doCenterline()

    def edgeFound(self, result, pointClicked):

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        self.segmentFinderTool.deactivate()

        if self.step == 0 or self.step == 2:
            self.snappingResults.append(result)
            self.snappingPointsClicked.append(pointClicked)

            self.rubberBandSelectedSegment.reset(QGis.Line)
            self.rubberBandSelectedSegment.addPoint(result.beforeVertex)
            self.rubberBandSelectedSegment.addPoint(result.afterVertex, True)
            self.rubberBandSelectedSegment.show()

            self.step += 1
        else:
            # step 1 or 3
            p = self._getSubPolyline(self.snappingResults[-1], result,
                                     self.snappingPointsClicked[0])
            if p:
                self.subPolylines.append(p)

                self.snappingResults.append(result)
                self.snappingPointsClicked.append(pointClicked)

                self.rubberBandSelectedSegment.reset(QGis.Line)
                self.rubberBandSelectedLine.reset(QGis.Line)

                if self.step == 1:
                    # don't draw when step is 3
                    polyline = QgsGeometry.fromPolyline(p)
                    self.rubberBandSelectedLine.setToGeometry(
                        polyline, self.snappingResults[0].layer)
                    self.rubberBandSelectedLine.show()

                self.step += 1
            else:
                # not valid choice for second segment: not on same layer or feature or subpolyline (if multipolyline)
                self.messageBarUtils.showMessage(
                    "Centerline",
                    "Invalid choice for second segment: Select a segment in the same part of the same geometry as the first",
                    QgsMessageBar.WARNING,
                    duration=5)

        self.next()

    def lastEdgeFound(self, candidateLayers, combobox, result, pointClicked):
        try:
            index = combobox.currentIndex()
            self.defaultIndex = index
            self.outputLayer = candidateLayers[index]
        except:
            # can happen in message with comobobox is closed by the user
            self.outputLayer = candidateLayers[self.defaultIndex]

        self.edgeFound(result, pointClicked)

    def listCandidateLayers(self, onlyEditable=True):
        candidates = []
        candidateLayers = []
        defaultIndex = 0
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            if (layer.type() == QgsMapLayer.VectorLayer
                    and layer.geometryType() == QGis.Line
                    and (not onlyEditable or layer.isEditable())):
                candidates.append(layer.name())
                candidateLayers.append(layer)
        sortedCandidates = sorted(enumerate(candidates), key=itemgetter(1))
        candidates = map(itemgetter(1), sortedCandidates)
        candidateLayers = map(lambda x: candidateLayers[x[0]],
                              sortedCandidates)
        for index in range(len(candidateLayers)):
            if candidateLayers[index] == self.layer:
                defaultIndex = index
        return candidates, candidateLayers, defaultIndex

    def doCenterline(self):
        self.messageBarUtils.showMessage("Centerline",
                                         "Running...",
                                         duration=0)
        try:
            qDebug("New Centerline Advanced")

            p1 = self.subPolylines[0]
            p2 = self.subPolylines[1]

            if self._mustReverse(p1, p2):
                # reverse the node order of any of the 2
                p1 = list(reversed(p1))

            # both input geometries and output layer are in same CRS
            if len(p1) == len(p2):
                p = self._processSimple(p1, p2)
            else:
                # voronoi with post processing
                p = self._processVoronoiPolygons(p1, p2)

            if p:
                self.outputLayer.beginEditCommand("Centerline")

                fieldCount = self.outputLayer.dataProvider().fields().count()
                feature = QgsFeature()
                feature.initAttributes(fieldCount)
                feature.setGeometry(p)
                self.outputLayer.addFeature(feature)

                self.outputLayer.endEditCommand()
                self.iface.mapCanvas().refresh()

                self.messageBarUtils.showMessage(
                    "Centerline",
                    "The centerline was created successfully",
                    duration=2)
            else:
                self.messageBarUtils.showMessage("Centerline",
                                                 "No centerline was created",
                                                 QgsMessageBar.WARNING,
                                                 duration=2)

        except Exception as e:
            raise
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Centerline",
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.outputLayer.destroyEditCommand()
            self.done()

        self.resetCenterline()
        self.next()

    def _processSimple(self, p1, p2):
        #just iterate
        p = []
        for i in range(len(p1)):
            endSegment = [p1[i], p2[i]]
            # middle of each end segments
            centerlineEndPoint = QgsPoint(
                (endSegment[0].x() + endSegment[1].x()) / 2,
                (endSegment[0].y() + endSegment[1].y()) / 2)
            p.append(centerlineEndPoint)
        return QgsGeometry.fromPolyline(p)

    def _processVoronoiPolygons(self, p1, p2):
        tempDir = None
        try:
            # define a polygon formed by p1 and p2: add edge at both ends to
            # close it. P1 and p2 have already been ordered to face each other
            # in the same direction
            ring = p1[0:]
            ring.extend([p1[-1], p2[-1]])
            ring.extend(p2[-1::-1])
            ring.extend([p2[0], p1[0]])
            pipePolygon = QgsGeometry.fromPolygon([ring])
            end1 = QgsGeometry.fromPolyline([p1[-1], p2[-1]])
            end2 = QgsGeometry.fromPolyline([p2[0], p1[0]])

            # create temp shp
            tempDir = tempfile.mkdtemp()
            vl = QgsVectorLayer(
                "LineString?crs=%s" % self.outputLayer.crs().toWkt(), "temp",
                "memory")
            pr = vl.dataProvider()
            vl.startEditing()
            pr.addAttributes([QgsField("id", QVariant.Int, "Integer")])
            vl.commitChanges()

            vl.startEditing()
            for p in [p1, p2]:
                feature = QgsFeature()
                feature.initAttributes(1)
                feature.setGeometry(QgsGeometry.fromPolyline(p))
                vl.addFeature(feature)
            vl.commitChanges()

            qDebug("mem " + repr(vl.featureCount()))

            inputShp = os.path.join(tempDir, "input.shp")
            QgsVectorFileWriter.writeAsVectorFormat(vl, inputShp, "UTF-8",
                                                    vl.crs())

            #             vl=QgsVectorLayer("Polygon?crs=%s" % self.outputLayer.crs().toWkt(), "temp3", "memory")
            #             pr = vl.dataProvider()
            #             vl.startEditing()
            #             pr.addAttributes([QgsField("id",QVariant.Int,"Integer")])
            #             vl.commitChanges()
            #             vl.startEditing()
            #             feature=QgsFeature()
            #             feature.initAttributes(1)
            #             feature.setGeometry(pipePolygon)
            #             vl.addFeature(feature)
            #             vl.commitChanges()
            # QgsMapLayerRegistry.instance().addMapLayer(vl)

            # processing often fails randomly
            # try multiple times
            trials = 0
            success = False
            while trials < 3 and not success:
                try:
                    output = processing.runalg("qgis:densifygeometries",
                                               inputShp, 100, None)
                    output = processing.runalg("qgis:extractnodes",
                                               output["OUTPUT"], None)
                    output = processing.runalg("qgis:voronoipolygons",
                                               output["OUTPUT"], 0.001, None)
                    voronoi = QgsVectorLayer(output["OUTPUT"], "voronoi",
                                             "ogr")
                    #                     QgsMapLayerRegistry.instance().addMapLayer(voronoi, addToLegend=True)
                    #                     voronoi.setLayerTransparency(50)

                    output = processing.runalg("qgis:extractnodes", inputShp,
                                               None)
                    originalNodes = QgsVectorLayer(output["OUTPUT"],
                                                   "original", "ogr")

                    success = originalNodes.featureCount > 0 and voronoi.featureCount(
                    ) > 0
                except:
                    trials += 1

            if not success:
                return None

            qDebug("after processing")

            # 1. create a spatial index with all the voronoi polygons
            # 2. Get the lsit of edges from the vornoi polygons that are in the pipePolygon
            voronoiEdges = []
            voronoiEdgeInsideSet = set()
            spatialIndex = QgsSpatialIndex()
            for f in voronoi.getFeatures():
                spatialIndex.insertFeature(f)
                geometry = f.geometry()

                # voronoi polygons are single parts with one ring
                p = geometry.asPolygon()
                for edgeP in self._extractEdgesFromPolyline(p[0]):
                    edge = QgsGeometry.fromPolyline(edgeP)
                    if edge.within(pipePolygon):
                        key = self._keyFromEdge(edgeP)
                        if key not in voronoiEdgeInsideSet:
                            # prevent duplicates (voronoi polygons share a border)
                            voronoiEdges.append(edgeP)
                            voronoiEdgeInsideSet.add(key)

            voronoiPolygonMajorEdges = []
            qDebug(repr(originalNodes.featureCount()))
            for originalNode in originalNodes.getFeatures():
                geometry = originalNode.geometry()
                candidates = spatialIndex.intersects(geometry.boundingBox())
                if len(candidates) > 0:
                    for candidate in candidates:
                        fCandidate = voronoi.getFeatures(
                            QgsFeatureRequest(candidate)).next()
                        gCandidate = fCandidate.geometry()
                        if geometry.within(gCandidate):
                            p = gCandidate.asPolygon()
                            edges = self._extractEdgesFromPolyline(p[0])
                            for edge in edges:
                                key = self._keyFromEdge(edge)
                                if key not in voronoiEdgeInsideSet:
                                    # do not add the edge if already in the centerline
                                    #gEdge=QgsGeometry.fromPolyline(edge)
                                    #if gEdge.intersects(pipePolygon):
                                    voronoiPolygonMajorEdges.append(key)
                            break

            qDebug("after spatindex")

            # recursively combine voronoiedges into one big polyline
            voronoiEdges = QgsGeometry.fromMultiPolyline(voronoiEdges)
            centerlinePolyline = voronoiEdges.combine(
                voronoiEdges)  #self.union(voronoiEdges)

            if centerlinePolyline.isMultipart():
                lines = centerlinePolyline.asMultiPolyline()
            else:
                lines = [centerlinePolyline.asPolyline()]

            qDebug("after merge")

            # key template
            kT = "%3.11f"

            nodeToVertexIndex = {}
            nodeCount = {}
            for i in range(len(lines)):
                line = lines[i]
                for j in range(len(line)):
                    point = line[j]
                    key = (kT % point.x(), kT % point.y())
                    count = nodeCount.get(key, 0)
                    if j <> 0 and j <> len(line) - 1:
                        # each of those nodes is linked to 2 segments
                        count += 2
                    else:
                        count += 1
                    nodeCount[key] = count

                    nodeToVertexIndex[key] = (i, j)


#             for i in range(len(lines)):
#                 line=lines[i]
#                 for j in range(len(line)):
#                     point=line[j]
#                     key=(kT%point.x(),kT%point.y())
#                     qDebug(repr(key)+" "+repr(nodeCount[key]))

            for edgeKey in voronoiPolygonMajorEdges:
                p1x, p1y, p2x, p2y = edgeKey
                for key in [(kT % p1x, kT % p1y), (kT % p2x, kT % p2y)]:
                    if key == ('-76.87093621506', '42.15309787572'):
                        qDebug("found mine " + repr(nodeCount.get(key, 0)))
                    count = nodeCount.get(key, 0)
                    if count == 2:
                        # only counts if in the main segment (not the branches)
                        nodeCount[key] = count + 1

            qDebug("after indexing")

            # cleanup: only keep important vertices
            toKeep = set(filter(lambda x: nodeCount[x] > 2, nodeCount.keys()))
            toRemove = set(nodeToVertexIndex.keys()) - toKeep
            toRemoveIndices = map(lambda x: nodeToVertexIndex[x], toRemove)
            for lineIndex, pointIndex in sorted(toRemoveIndices,
                                                cmp=self._compareIndices,
                                                reverse=True):
                line = lines[lineIndex]
                del line[pointIndex]

            self._cleanupDegenerateSegments(lines)

            qDebug("after cleanup")

            return self.union(map(lambda x: QgsGeometry.fromPolyline(x),
                                  lines))
        finally:
            if tempDir:
                pass  #shutil.rmtree(tempDir, True)
            qDebug("after union")

    def union(self, lines):
        return reduce(lambda m, x: m.combine(x), lines[1:], lines[0])

    def _compareIndices(self, x, y):
        i1, j1 = x
        i2, j2 = y
        if i1 < i2:
            return -1
        elif i2 < i1:
            return 1
        else:
            if j1 < j2:
                return -1
            elif j2 < j1:
                return 1
            else:
                return 0

    def _keyFromEdge(self, edge):
        return (edge[0].x(), edge[0].y(), edge[1].x(), edge[1].y())

    def _cleanupDegenerateSegments(self, lines):
        linesToRemove = []
        for i in range(len(lines)):
            line = lines[i]
            if len(line) <= 1:
                linesToRemove.append(i)

        for i in reversed(linesToRemove):
            del lines[i]

        return lines

    def _extractEdgesFromPolyline(self, p):
        edges = []
        for i in range(len(p) - 1):
            edgeP = p[i:i + 2]
            edges.append(edgeP)
        return edges

    def _mustReverse(self, p1, p2):
        polyline1 = QgsGeometry.fromPolyline(p1)
        polyline2 = QgsGeometry.fromPolyline(p2)

        candidates1 = (QgsGeometry.fromPolyline([p1[0], p2[0]]),
                       QgsGeometry.fromPolyline([p1[-1], p2[-1]]))

        return ((candidates1[0].intersects(candidates1[1])
                 and not candidates1[0].equals(candidates1[1]))
                or polyline1.crosses(candidates1[0])
                or polyline1.crosses(candidates1[1])
                or polyline2.crosses(candidates1[0])
                or polyline2.crosses(candidates1[1]))

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = snappingResult.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def _getSegment(self, snappingResult):
        feature = self._getFeature(snappingResult)
        geometry = feature.geometry()
        bv = geometry.vertexAt(snappingResult.beforeVertexNr)
        av = geometry.vertexAt(snappingResult.afterVertexNr)
        return QgsGeometry.fromPolyline([bv, av])

    def _getSubPolyline(self, snappingResult1, snappingResult2, pointClicked1):
        """
        computes the polyline defined by the 2 segments: all the segments between those 2
        will be included in the returned polyline. If Ctrl held while selecting the 2nd segment, 
        The side of the first segment the user clicked on determines which direction to go to add those segments
        """
        if (snappingResult1.layer.id() <> snappingResult2.layer.id()
                or snappingResult1.snappedAtGeometry <>
                snappingResult2.snappedAtGeometry):
            return None

        feature = self._getFeature(snappingResult1)
        geometry = feature.geometry()

        if geometry.isMultipart():
            mp = geometry.asMultipolyline()
            p, relative = vectorlayerutils.polylineWithVertexAtIndex(
                mp, snappingResult1.beforeVertexNr)
            _, relative2 = vectorlayerutils.polylineWithVertexAtIndex(
                mp, snappingResult2.beforeVertexNr)
            if relative <> relative2:
                # not on the same sub polyline
                return None
        else:
            p = geometry.asPolyline()
            relative = 0

        if self.orderSelection:
            bv = geometry.vertexAt(snappingResult1.beforeVertexNr)
            av = geometry.vertexAt(snappingResult1.afterVertexNr)

            # get the direction in which to add segments
            # vertex closest to point clicked is the top vertex: direction will be from
            # the other vertex to that one
            extendUtils = ExtendUtils(self.iface)
            vertexNrDirection = extendUtils.vertexIndexToMove(bv,
                                                              av,
                                                              snappingResult1,
                                                              pointClicked1,
                                                              mustExtend=False)

            # positive if index of vertices is incremented
            direction = vertexNrDirection == snappingResult1.afterVertexNr

            if not direction:
                # swap
                temp = snappingResult2
                snappingResult2 = snappingResult1
                snappingResult1 = temp

            if snappingResult2.afterVertexNr <= snappingResult1.beforeVertexNr:
                # wrap around
                nodes = p[snappingResult1.beforeVertexNr - relative:]
                nodes.extend(p[0:snappingResult2.afterVertexNr - relative + 1])
            else:
                nodes = p[snappingResult1.beforeVertexNr -
                          relative:snappingResult2.afterVertexNr - relative +
                          1]
        else:
            if snappingResult2.beforeVertexNr < snappingResult1.beforeVertexNr:
                # swap
                temp = snappingResult2
                snappingResult2 = snappingResult1
                snappingResult1 = temp
                direction = False
            else:
                direction = True

            nodes = p[snappingResult1.beforeVertexNr -
                      relative:snappingResult2.afterVertexNr - relative + 1]

        qDebug(repr(nodes))

        return nodes if direction else list(reversed(nodes))

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.done()
                return

        self.orderSelection = e.modifiers() == Qt.ControlModifier
        qDebug(repr(self.orderSelection))

        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)

    def snap(self, pos):
        (_, result) = self.snapper.snapToBackgroundLayers(pos)
        return result
コード例 #18
0
ファイル: fillet.py プロジェクト: gvellut/GVDigitizingTools
class FilletDigitizingMode(QgsMapToolEmitPoint):

    MESSAGE_HEADER = "Fillet"
    DEFAULT_FILLET_RADIUS = 0

    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)
        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())
        self.rubberBandSegment1 = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSegment1.setColor(QColor(255, 0, 0))
        self.rubberBandSegment1.setWidth(2)

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0

        self.segment1 = None
        self.segment2 = None

        self.rubberBandSegment1.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def resetFillet(self):
        self.reset(False)

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showButton(
                FilletDigitizingMode.MESSAGE_HEADER,
                "Select first segment",
                "Done",
                buttonCallback=self.done)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.segment1Found)

        elif self.step == 1:

            filletRadius, found = QgsProject.instance().readEntry(
                constants.SETTINGS_KEY, constants.SETTINGS_FILLET_RADIUS, None)

            if not found:
                filletRadius = str(FilletDigitizingMode.DEFAULT_FILLET_RADIUS)

            _, lineEdit = self.messageBarUtils.showLineEdit(
                FilletDigitizingMode.MESSAGE_HEADER,
                "Select second segment and set radius:", filletRadius)
            self.lineEditFilletRadius = lineEdit

            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.segment2Found)

        elif self.step == 2:
            self.filletRadius = self.lineEditFilletRadius.text()

            self.doFillet()

    def segment1Found(self, result, pointClicked):
        self.segment1 = result
        self.segmentFinderTool.segmentFound.disconnect(self.segment1Found)
        self.segmentFinderTool.deactivate()

        self.rubberBandSegment1.reset(QGis.Line)
        self.rubberBandSegment1.addPoint(result.beforeVertex)
        self.rubberBandSegment1.addPoint(result.afterVertex, True)
        self.rubberBandSegment1.show()

        self.step = 1
        self.next()

    def segment2Found(self, result, pointClicked):
        self.segment2 = result
        self.segmentFinderTool.segmentFound.disconnect(self.segment2Found)
        self.segmentFinderTool.deactivate()

        self.step = 2
        self.next()

    def doFillet(self):
        self.messageBarUtils.showMessage(FilletDigitizingMode.MESSAGE_HEADER,
                                         "Running...",
                                         QgsMessageBar.INFO,
                                         duration=0)

        try:

            crsDest = self.layer.crs()
            canvas = self.iface.mapCanvas()
            mapRenderer = canvas.mapSettings()
            crsSrc = mapRenderer.destinationCrs()
            crsTransform = QgsCoordinateTransform(crsSrc, crsDest)

            self.layer.beginEditCommand(FilletDigitizingMode.MESSAGE_HEADER)

            try:
                filletRadius = float(self.filletRadius)
                # save for next time
                QgsProject.instance().writeEntry(
                    constants.SETTINGS_KEY, constants.SETTINGS_FILLET_RADIUS,
                    filletRadius)
            except ValueError:
                filletRadius = FilletDigitizingMode.DEFAULT_FILLET_RADIUS

            # do initial computations in map canvas coordinates
            # check intersection

            extendUtils = ExtendUtils(self.iface)
            ip = extendUtils.intersectionPoint(self.segment1.beforeVertex,
                                               self.segment1.afterVertex,
                                               self.segment2.beforeVertex,
                                               self.segment2.afterVertex)

            if ip == None:
                self.messageBarUtils.showMessage(
                    FilletDigitizingMode.MESSAGE_HEADER,
                    "The 2 segments do not intersect. Nothing was done.",
                    QgsMessageBar.WARNING,
                    duration=5)

                self.layer.destroyEditCommand()
                return

            if filletRadius == 0:
                # reproject to layer coordinates
                ip = crsTransform.transform(ip)

                # just extend the 2 lines
                ok1 = self.extend(ip, self.segment1, extendUtils)
                ok2 = self.extend(ip, self.segment2, extendUtils)

                if ok1 and ok2:
                    self.iface.mapCanvas().refresh()
                    self.messageBarUtils.showMessage(
                        FilletDigitizingMode.MESSAGE_HEADER, "Success",
                        QgsMessageBar.INFO, 5)
                    self.layer.endEditCommand()
                else:
                    self.messageBarUtils.showMessage(
                        FilletDigitizingMode.MESSAGE_HEADER,
                        "Cannot fillet segments: Amibiguous extension",
                        QgsMessageBar.WARNING, 5)
                    self.layer.destroyEditCommand()

            else:

                (pa, p1, p2, revert1, revert2) = self.computeArcParameters(
                    ip, self.segment1, self.segment2, filletRadius,
                    extendUtils)

                if pa == None:
                    # radius is too big
                    self.messageBarUtils.showMessage(
                        FilletDigitizingMode.MESSAGE_HEADER,
                        "A fillet cannot be created with those arcs.",
                        QgsMessageBar.WARNING,
                        duration=5)
                    return

                settings = QSettings()
                method = settings.value(constants.ARC_METHOD, "")
                if method == constants.ARC_METHOD_NUMBEROFPOINTS:
                    value = settings.value(constants.ARC_NUMBEROFPOINTS,
                                           0,
                                           type=int)
                else:
                    value = settings.value(constants.ARC_ANGLE, 0, type=float)

                # g is still in CRS of map
                g = CircularArc.getInterpolatedArc(p1, pa, p2, method, value)
                arcVertices = g.asPolyline()

                if len(arcVertices) >= 2:

                    projArcVertices = []
                    for vertex in arcVertices:
                        projArcVertices.append(crsTransform.transform(vertex))

                    outFeat = QgsFeature()

                    fields = self.layer.dataProvider().fields()
                    outFeat.setAttributes([None] * fields.count())
                    outFeat.setGeometry(
                        QgsGeometry.fromPolyline(projArcVertices))
                    self.layer.addFeature(outFeat)

                    # trim segments
                    p1 = crsTransform.transform(p1)
                    feature1 = self.getFeature(self.segment1)
                    geom1 = feature1.geometry()
                    geom1.insertVertex(p1.x(), p1.y(),
                                       self.segment1.afterVertexNr)
                    if revert1:
                        geom1.deleteVertex(self.segment1.afterVertexNr +
                                           1)  # insertion above => +1
                    else:
                        geom1.deleteVertex(self.segment1.beforeVertexNr)
                    self.layer.changeGeometry(feature1.id(), geom1)

                    p2 = crsTransform.transform(p2)
                    feature2 = self.getFeature(self.segment2)
                    geom2 = feature2.geometry()
                    geom2.insertVertex(p2.x(), p2.y(),
                                       self.segment2.afterVertexNr)
                    if revert2:
                        geom2.deleteVertex(self.segment2.afterVertexNr +
                                           1)  # insertion above => +1
                    else:
                        geom2.deleteVertex(self.segment2.beforeVertexNr)
                    self.layer.changeGeometry(feature2.id(), geom2)

                    self.iface.mapCanvas().refresh()
                    self.messageBarUtils.showMessage(
                        FilletDigitizingMode.MESSAGE_HEADER, "Success",
                        QgsMessageBar.INFO, 5)

                    self.layer.endEditCommand()

                else:
                    self.messageBarUtils.showMessage(
                        FilletDigitizingMode.MESSAGE_HEADER,
                        "No arc was created",
                        QgsMessageBar.WARNING,
                        duration=5)
                    self.layer.destroyEditCommand()

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.removeAllMessages()
            self.messageBarUtils.showMessage(
                FilletDigitizingMode.MESSAGE_HEADER,
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.layer.destroyEditCommand()

            return
        finally:
            # select another fillet
            self.resetFillet()
            self.next()

    def extend(self, ip, snapSegment, extendUtils):
        featureExtend = self.getFeature(snapSegment)
        geometryExtend = featureExtend.geometry()
        bvExtend = geometryExtend.vertexAt(snapSegment.beforeVertexNr)
        avExtend = geometryExtend.vertexAt(snapSegment.afterVertexNr)

        number = extendUtils.vertexIndexToMove(bvExtend, avExtend, snapSegment,
                                               ip)
        if number == None:
            # not an extend
            return False
        else:
            fid = featureExtend.id()
            geometryExtend.moveVertex(ip.x(), ip.y(), number)
            self.layer.changeGeometry(fid, geometryExtend)
            return True

    def computeArcParameters(self, ip, snapSegment1, snapSegment2,
                             filletRadius, extendUtils):

        bvExtend1 = snapSegment1.beforeVertex
        avExtend1 = snapSegment1.afterVertex
        # TODO => mustExtend =True and check the 2 segment have the same endpoint at ip (with some tolerance)
        iVToMove1 = extendUtils.vertexIndexToMove(bvExtend1,
                                                  avExtend1,
                                                  snapSegment1,
                                                  ip,
                                                  mustExtend=False)

        bvExtend2 = snapSegment2.beforeVertex
        avExtend2 = snapSegment2.afterVertex
        iVToMove2 = extendUtils.vertexIndexToMove(bvExtend2,
                                                  avExtend2,
                                                  snapSegment2,
                                                  ip,
                                                  mustExtend=False)

        qDebug(repr(iVToMove1) + " " + repr(iVToMove2))

        if iVToMove1 == None or iVToMove2 == None:
            return (None, None, None, None, None)

        segment1 = [bvExtend1, avExtend1]
        segment2 = [bvExtend2, avExtend2]

        # determine intersection point (ip) and revert if needed
        revert1 = False
        revert2 = False
        if iVToMove1 == snapSegment1.beforeVertexNr:
            segment1[0] = ip
        else:
            revert1 = True
            segment1[1] = segment1[0]
            segment1[0] = ip

        if iVToMove2 == snapSegment2.beforeVertexNr:
            segment2[0] = ip
        else:
            revert2 = True
            segment2[1] = segment2[0]
            segment2[0] = ip

        lengthSegment1 = self.length(segment1)
        normedSegment1 = self.norm(segment1, lengthSegment1)
        lengthSegment2 = self.length(segment2)
        normedSegment2 = self.norm(segment2, lengthSegment2)

        midp1 = self.pointAtDist(normedSegment1, filletRadius)
        midp2 = self.pointAtDist(normedSegment2, filletRadius)

        # get point on bisector
        midp = QgsPoint((midp1.x() + midp2.x()) / 2.0,
                        (midp1.y() + midp2.y()) / 2.0)
        # get angles along lines from intersection
        ang1 = self.angleFromX(ip, midp1)
        ang2 = self.angleFromX(ip, midp2)

        # get bisector angle
        ang = self.angleFromX(ip, midp)

        # get a half of angle between segments
        bis = abs(ang2 - ang1) / 2.0
        # calculate hypotenuse (fillet draws slice of circle tangent to segments)
        hyp = filletRadius / math.sin(bis)

        # calculate another leg of a triangle
        cat = math.sqrt(hyp**2 - filletRadius**2)

        if cat > self.length(segment1) or cat > self.length(segment2):
            # radius is too big => nothing done
            return (None, None, None, None, None)

        # calculate point on arc
        pa = self.polarPoint(ip, ang, hyp - filletRadius)
        # calculate start point of arc
        p1 = self.polarPoint(ip, ang1, cat)
        # calculate end point of arc
        p2 = self.polarPoint(ip, ang2, cat)

        return (pa, p1, p2, revert1, revert2)

    def length(self, segment):
        return math.sqrt((segment[0].x() - segment[1].x())**2 +
                         (segment[0].y() - segment[1].y())**2)

    def norm(self, segment, length):
        return [
            segment[0],
            QgsPoint(
                segment[0].x() + (segment[1].x() - segment[0].x()) / length,
                segment[0].y() + (segment[1].y() - segment[0].y()) / length)
        ]

    def pointAtDist(self, normedSegment, dist):
        return QgsPoint(
            normedSegment[0].x() + dist *
            (normedSegment[1].x() - normedSegment[0].x()),
            normedSegment[0].y() + dist *
            (normedSegment[1].y() - normedSegment[0].y()))

    def polarPoint(self, basepoint, angle, distance):
        return QgsPoint(basepoint.x() + (distance * math.cos(angle)),
                        basepoint.y() + (distance * math.sin(angle)))

    def angleFromX(self, pt1, pt2):
        dx = pt2.x() - pt1.x()
        dy = pt2.y() - pt1.y()
        ang = math.atan2(dy, dx)
        if ang < 0.0:
            return ang + 2.0 * math.pi
        else:
            return ang

    def getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = self.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.done()
                return

        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)
コード例 #19
0
class CenterlineDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())
        self.rubberBandSelectedSegment = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedSegment.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedSegment.setWidth(2)

        self.defaultIndex = None
        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0
        self.snappingResultFirstEdge = None
        self.snappingResultSecondEdge = None
        self.defaultIndex = None

        self.rubberBandSelectedSegment.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def resetCenterline(self):
        self.step = 0
        self.snappingResultFirstEdge = None
        self.snappingResultSecondEdge = None
        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.segmentFinderTool.layers = None

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showButton("Centerline",
                                            "Select first edge",
                                            "Done",
                                            buttonCallback=self.done)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.firstEdgeFound)
        elif self.step == 1:
            candidates, candidateLayers, defaultIndex = self.listCandidateLayers(
            )
            if self.defaultIndex <> None and defaultIndex < len(
                    candidateLayers):
                defaultIndex = self.defaultIndex
            _, combobox = self.messageBarUtils.showCombobox(
                "Centerline", "Select second edge", candidates, defaultIndex)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(
                partial(self.secondEdgeFound, candidateLayers, combobox))
        elif self.step == 2:
            self.doCenterline()

    def firstEdgeFound(self, result, pointClicked):
        self.snappingResultFirstEdge = result

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        self.segmentFinderTool.deactivate()

        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.rubberBandSelectedSegment.addPoint(result.beforeVertex)
        self.rubberBandSelectedSegment.addPoint(result.afterVertex, True)
        self.rubberBandSelectedSegment.show()

        self.step = 1
        self.next()

    def secondEdgeFound(self, candidateLayers, combobox, result, pointClicked):
        index = combobox.currentIndex()
        self.defaultIndex = index
        self.outputLayer = candidateLayers[index]

        self.snappingResultSecondEdge = result

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        self.segmentFinderTool.deactivate()

        self.step = 2
        self.next()

    def listCandidateLayers(self, onlyEditable=True):
        candidates = []
        candidateLayers = []
        defaultIndex = 0
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            if (layer.type() == QgsMapLayer.VectorLayer
                    and layer.geometryType() == QGis.Line
                    and (not onlyEditable or layer.isEditable())):
                candidates.append(layer.name())
                candidateLayers.append(layer)
        sortedCandidates = sorted(enumerate(candidates), key=itemgetter(1))
        candidates = map(itemgetter(1), sortedCandidates)
        candidateLayers = map(lambda x: candidateLayers[x[0]],
                              sortedCandidates)
        for index in range(len(candidateLayers)):
            if candidateLayers[index] == self.layer:
                defaultIndex = index
        return candidates, candidateLayers, defaultIndex

    def doCenterline(self):
        self.messageBarUtils.showMessage("Centerline",
                                         "Running...",
                                         duration=0)
        try:
            qDebug("New Centerline")

            firstSegment = self._getSegment(self.snappingResultFirstEdge)
            secondSegment = self._getSegment(self.snappingResultSecondEdge)
            endSegments = self._getEndSegments(firstSegment, secondSegment)

            # middle of each end segments
            centerlineEndPoint1 = QgsPoint(
                (endSegments[0].vertexAt(0).x() +
                 endSegments[0].vertexAt(1).x()) / 2,
                (endSegments[0].vertexAt(0).y() +
                 endSegments[0].vertexAt(1).y()) / 2)
            centerlineEndPoint2 = QgsPoint(
                (endSegments[1].vertexAt(0).x() +
                 endSegments[1].vertexAt(1).x()) / 2,
                (endSegments[1].vertexAt(0).y() +
                 endSegments[1].vertexAt(1).y()) / 2)
            line = QgsGeometry.fromPolyline(
                [centerlineEndPoint1, centerlineEndPoint2])
            self.outputLayer.beginEditCommand("Centerline")
            feature = QgsFeature()
            feature.initAttributes(
                self.outputLayer.dataProvider().fields().count())
            feature.setGeometry(line)
            self.outputLayer.addFeature(feature)

            self.outputLayer.endEditCommand()

            self.iface.mapCanvas().refresh()

            self.messageBarUtils.showMessage(
                "Centerline",
                "The centerline was created successfully",
                duration=2)
        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Centerline",
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.outputLayer.destroyEditCommand()
            self.done()

        self.resetCenterline()
        self.next()

    def _getEndSegments(self, firstSegment, secondSegment):
        candidates1 = (QgsGeometry.fromPolyline(
            [firstSegment.vertexAt(0),
             secondSegment.vertexAt(0)]),
                       QgsGeometry.fromPolyline([
                           firstSegment.vertexAt(1),
                           secondSegment.vertexAt(1)
                       ]))
        candidates2 = (QgsGeometry.fromPolyline(
            [firstSegment.vertexAt(0),
             secondSegment.vertexAt(1)]),
                       QgsGeometry.fromPolyline([
                           firstSegment.vertexAt(1),
                           secondSegment.vertexAt(0)
                       ]))
        if candidates1[0].intersects(candidates1[1]):
            return candidates2
        else:
            return candidates1

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = snappingResult.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def _getSegment(self, snappingResult):
        feature = self._getFeature(snappingResult)
        geometry = feature.geometry()
        bv = geometry.vertexAt(snappingResult.beforeVertexNr)
        av = geometry.vertexAt(snappingResult.afterVertexNr)
        return QgsGeometry.fromPolyline([bv, av])

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.done()
                return

        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)
コード例 #20
0
ファイル: mirror.py プロジェクト: gvellut/GVDigitizingTools
class MirrorDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface, digitizingTools):
        self.iface = iface
        self.digitizingTools = digitizingTools
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.rubberBandMirrorAxis = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandMirrorAxis.setColor(QColor(255, 0, 0))
        self.rubberBandMirrorAxis.setWidth(2)

        self.rubberBandGeometriesPreview = QgsRubberBand(
            self.iface.mapCanvas())
        self.rubberBandGeometriesPreview.setColor(QColor(0, 255, 0))
        self.rubberBandGeometriesPreview.setWidth(1)

        self.rubberBandSnap = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSnap.setColor(QColor(255, 51, 153))
        self.rubberBandSnap.setIcon(QgsRubberBand.ICON_CROSS)
        self.rubberBandSnap.setIconSize(12)
        self.rubberBandSnap.setWidth(3)

        self.isEmittingPoint = False

        # we need a snapper, so we use the MapCanvas snapper
        self.snapper = QgsMapCanvasSnapper(self.iface.mapCanvas())

        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

        crsDest = self.layer.crs()
        canvas = self.iface.mapCanvas()
        mapRenderer = canvas.mapSettings()
        crsSrc = mapRenderer.destinationCrs()
        self.crsTransform = QgsCoordinateTransform(crsSrc, crsDest)
        # cannot indicate direction in QgsGeometry.transform
        self.crsTransformReverse = QgsCoordinateTransform(crsDest, crsSrc)

    def reset(self, clearMessages=True):
        self.step = 0

        self.rubberBandMirrorAxis.reset(QGis.Line)
        self.rubberBandGeometriesPreview.reset(QGis.Line)
        self.rubberBandSnap.reset(QGis.Point)

        settings = QSettings()
        self.isPreviewEnabled = settings.value(constants.PREVIEW_ENABLED,
                                               type=bool)
        self.isLimitPreviewEnabled = settings.value(
            constants.PREVIEW_LIMIT_ENABLED, type=bool)
        self.maxLimitPreview = settings.value(constants.PREVIEW_LIMIT_MAX,
                                              type=int)

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def deactivate(self):
        self.reset()
        super(QgsMapToolEmitPoint, self).deactivate()

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showMessage(
                "Mirror", "Select first point of mirror line", duration=0)
        elif self.step == 1:
            self.messageBarUtils.removeAllMessages()
            self.messageBarUtils.showMessage(
                "Mirror", "Select second point of mirror line", duration=0)
        elif self.step == 2:
            if self.layer.selectedFeatureCount() == 0:
                self.messageBarUtils.showYesCancel(
                    "Mirror",
                    "No feature selected in Layer %s. Proceed on full layer?" %
                    self.layer.name(), QgsMessageBar.WARNING, self.doMirror,
                    self.deactivate)
                return

            self.doMirror()

    def doMirror(self):

        _, progress = self.messageBarUtils.showProgress(
            "Mirror", "Running...", QgsMessageBar.INFO)

        try:

            self.rubberBandGeometriesPreview.reset(QGis.Line)

            self.layer.beginEditCommand("Mirror")

            ok = self.prepareMirror()

            if not ok:
                self.messageBarUtils.showMessage("Mirror",
                                                 "Invalid mirror line.",
                                                 QgsMessageBar.WARNING,
                                                 duration=5)
                self.layer.destroyEditCommand()
                return

            self.iface.mapCanvas().freeze(True)

            outFeat = QgsFeature()

            current = 0
            features = vectorlayerutils.features(self.layer)
            total = 100.0 / float(len(features))
            pgidIndex = self.layer.fieldNameIndex("gid")
            for f in features:
                inGeom = f.geometry()
                attrs = f.attributes()

                # perform mirror in map coordinates instead of layer coordinates
                inGeom.transform(self.crsTransformReverse)

                # break into multiple geometries if needed
                if inGeom.isMultipart():
                    geometries = inGeom.asMultiPolyline()
                    outGeom = []

                    for g in geometries:
                        polyline = self.mirrorPolyline(g)
                        outGeom.append(polyline)

                    outGeom = QgsGeometry.fromMultiPolyline(outGeom)
                else:
                    polyline = self.mirrorPolyline(inGeom.asPolyline())
                    outGeom = QgsGeometry.fromPolyline(polyline)

                outGeom.transform(self.crsTransform)

                # beeeeeuh... Cannot make QPyNullVariant work
                # deleteAttribute with string is masked...
                # so...
                outFeat.initAttributes(len(attrs))
                for index in range(len(attrs)):
                    if index <> pgidIndex:
                        outFeat.setAttribute(index, attrs[index])
                outFeat.setGeometry(outGeom)
                ok = self.layer.addFeature(outFeat)
                current += 1
                progress.setValue(int(current * total))

            self.messageBarUtils.showMessage("Mirror", "Success",
                                             QgsMessageBar.INFO, 5)
            self.layer.endEditCommand()

        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Mirror",
                "There was an error performing this command. See QGIS Message log for details.",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.layer.destroyEditCommand()

        finally:
            self.iface.mapCanvas().freeze(False)
            self.iface.mapCanvas().refresh()
            self.iface.mapCanvas().unsetMapTool(self)

    def prepareMirror(self):
        # rubber bands are in crs of map canvas
        self.layerStartPoint = self.startPoint
        self.layerEndPoint = self.endPoint

        dx = self.layerEndPoint.x() - self.layerStartPoint.x()
        dy = self.layerEndPoint.y() - self.layerStartPoint.y()
        norm = sqrt(dx**2 + dy**2)
        if norm == 0:
            return False

        self.layerEndPoint = QgsPoint(self.layerStartPoint.x() + dx / norm,
                                      self.layerStartPoint.x() + dy / norm)
        # consider the origin at startPoint
        self.normedAxis = QgsPoint(dx / norm, dy / norm)
        return True

    def mirrorPolyline(self, polyline):
        for vertex in polyline:
            # project
            relSP = QgsPoint(vertex.x() - self.layerStartPoint.x(),
                             vertex.y() - self.layerStartPoint.y())
            dp = relSP.x() * self.normedAxis.x() + relSP.y(
            ) * self.normedAxis.y()
            vertex.setX(self.layerStartPoint.x() - relSP.x() +
                        2 * dp * self.normedAxis.x())
            vertex.setY(self.layerStartPoint.y() - relSP.y() +
                        2 * dp * self.normedAxis.y())

        return polyline

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        pass

    def canvasReleaseEvent(self, e):
        if self.step == 0:
            # we snap to the current layer (we don't have exclude points and use the tolerances from the qgis properties)
            pos = QPoint(e.pos().x(), e.pos().y())
            result = self.snap(pos)
            if result != []:
                self.startPoint = result[0].snappedVertex
            else:
                self.startPoint = self.toMapCoordinates(e.pos())

            self.endPoint = self.startPoint
            self.isEmittingPoint = True

            self.rubberBandMirrorAxis.reset(QGis.Line)
            self.rubberBandMirrorAxis.addPoint(self.startPoint)
            self.rubberBandMirrorAxis.addPoint(self.endPoint)
            self.rubberBandMirrorAxis.show()

            self.step = 1
            self.next()
        elif self.step == 1:
            pos = QPoint(e.pos().x(), e.pos().y())
            result = self.snap(pos)
            if result != []:
                self.endPoint = result[0].snappedVertex
            else:
                self.endPoint = self.toMapCoordinates(e.pos())

            self.isEmittingPoint = False

            self.step = 2
            self.next()

    def canvasMoveEvent(self, e):
        pos = QPoint(e.pos().x(), e.pos().y())
        result = self.snap(pos)
        self.rubberBandSnap.reset(QGis.Point)
        if result != []:
            self.rubberBandSnap.addPoint(result[0].snappedVertex, True)

        if not self.isEmittingPoint:
            return

        if self.step == 1:
            if result != []:
                self.endPoint = result[0].snappedVertex
            else:
                self.endPoint = self.toMapCoordinates(e.pos())
            self.rubberBandMirrorAxis.reset(QGis.Line)
            self.rubberBandMirrorAxis.addPoint(self.startPoint)
            self.rubberBandMirrorAxis.addPoint(self.endPoint)
            self.rubberBandMirrorAxis.show()

            self.updateGeometriesPreview()

    def updateGeometriesPreview(self):
        ok = self.prepareMirror()
        self.rubberBandGeometriesPreview.reset(QGis.Line)

        if not ok:
            return

        if not self.isPreviewEnabled:
            return

        if self.isLimitPreviewEnabled:
            maxCount = self.maxLimitPreview
        else:
            maxCount = self.layer.featureCount()

        currentCount = 0
        features = vectorlayerutils.features(self.layer)
        for f in features:
            inGeom = f.geometry()
            inGeom.transform(self.crsTransformReverse)

            # break into multiple geometries if needed
            if inGeom.isMultipart():
                geometries = inGeom.asMultiPolyline()

                for g in geometries:
                    polyline = self.mirrorPolyline(g)
                    # polyline in map coordinates
                    self.rubberBandGeometriesPreview.addGeometry(
                        QgsGeometry.fromPolyline(polyline), None)

            else:
                polyline = self.mirrorPolyline(inGeom.asPolyline())
                self.rubberBandGeometriesPreview.addGeometry(
                    QgsGeometry.fromPolyline(polyline), None)

            currentCount += 1
            if currentCount >= maxCount:
                break

        self.rubberBandGeometriesPreview.show()

    def snap(self, pos):
        if self.digitizingTools.isBackgroundSnapping:
            (_, result) = self.snapper.snapToBackgroundLayers(pos)
        else:
            (_,
             result) = self.snapper.snapToCurrentLayer(pos,
                                                       QgsSnapper.SnapToVertex)
        return result