class SplitMapTool(QgsMapToolEdit):
    def __init__(self, canvas, layer, actionMoveVertices, actionAddVertices,
                 actionRemoveVertices, actionMoveSegment, actionLineClose,
                 actionLineOpen, actionMoveLine):
        super(SplitMapTool, self).__init__(canvas)
        self.canvas = canvas
        self.scene = canvas.scene()
        self.layer = layer
        self.actionMoveVertices = actionMoveVertices
        self.actionAddVertices = actionAddVertices
        self.actionRemoveVertices = actionRemoveVertices
        self.actionMoveSegment = actionMoveSegment
        self.actionLineClose = actionLineClose
        self.actionLineOpen = actionLineOpen
        self.actionMoveLine = actionMoveLine
        self.initialize()

    def initialize(self):
        try:
            self.canvas.renderStarting.disconnect(self.mapCanvasChanged)
        except:
            pass
        self.canvas.renderStarting.connect(self.mapCanvasChanged)
        try:
            self.layer.editingStopped.disconnect(self.stopCapturing)
        except:
            pass
        self.layer.editingStopped.connect(self.stopCapturing)

        self.selectedFeatures = self.layer.selectedFeatures()
        self.rubberBand = None
        self.tempRubberBand = None
        self.capturedPoints = []
        self.capturing = False
        self.setCursor(Qt.CrossCursor)
        self.proj = QgsProject.instance()
        self.labels = []
        self.vertices = []
        self.calculator = QgsDistanceArea()
        self.calculator.setSourceCrs(self.layer.dataProvider().crs(),
                                     QgsProject.instance().transformContext())
        self.calculator.setEllipsoid(
            self.layer.dataProvider().crs().ellipsoidAcronym())
        self.drawingLine = False
        self.movingVertices = False
        self.addingVertices = False
        self.removingVertices = False
        self.movingSegment = False
        self.movingLine = False
        self.showingVertices = False
        self.movingVertex = -1
        self.movingSegm = -1
        self.movingLineInitialPoint = None
        self.lineClosed = False

    def restoreAction(self):
        self.addingVertices = False
        self.removingVertices = False
        self.movingVertices = False
        self.movingSegment = False
        self.movingLine = False
        self.showingVertices = False
        self.drawingLine = True
        self.movingVertex = -1
        self.movingLineInitialPoint = None
        self.deleteVertices()
        self.redrawRubberBand()
        self.redrawTempRubberBand()
        self.canvas.scene().addItem(self.tempRubberBand)
        self.redrawActions()

    def mapCanvasChanged(self):
        self.redrawAreas()
        if self.showingVertices:
            self.redrawVertices()

    def canvasMoveEvent(self, event):
        if self.drawingLine and not self.lineClosed:
            if self.tempRubberBand != None and self.capturing:
                mapPoint = self.toMapCoordinates(event.pos())
                self.tempRubberBand.movePoint(mapPoint)
                self.redrawAreas(event.pos())

        if self.movingVertices and self.movingVertex >= 0:
            layerPoint = self.toLayerCoordinates(self.layer, event.pos())
            self.capturedPoints[self.movingVertex] = layerPoint

            if self.lineClosed and self.movingVertex == 0:
                self.capturedPoints[len(self.capturedPoints) - 1] = layerPoint

            self.redrawRubberBand()
            self.redrawVertices()
            self.redrawAreas()

        if self.movingSegment and self.movingSegm >= 0:
            currentPoint = self.toLayerCoordinates(self.layer, event.pos())
            distance = self.distancePoint(currentPoint,
                                          self.movingLineInitialPoint)
            bearing = self.movingLineInitialPoint.azimuth(currentPoint)

            self.capturedPoints[self.movingSegm] = self.projectPoint(
                self.capturedPoints[self.movingSegm], distance, bearing)
            self.capturedPoints[self.movingSegm + 1] = self.projectPoint(
                self.capturedPoints[self.movingSegm + 1], distance, bearing)

            if self.lineClosed:
                if self.movingSegm == 0:
                    self.capturedPoints[
                        len(self.capturedPoints) - 1] = self.projectPoint(
                            self.capturedPoints[len(self.capturedPoints) - 1],
                            distance, bearing)
                elif self.movingSegm == len(self.capturedPoints) - 2:
                    self.capturedPoints[0] = self.projectPoint(
                        self.capturedPoints[0], distance, bearing)

            self.redrawRubberBand()
            self.redrawVertices()
            self.redrawAreas()
            self.movingLineInitialPoint = currentPoint

        if self.movingLine and self.movingLineInitialPoint != None:
            currentPoint = self.toLayerCoordinates(self.layer, event.pos())
            distance = self.distancePoint(currentPoint,
                                          self.movingLineInitialPoint)
            bearing = self.movingLineInitialPoint.azimuth(currentPoint)
            for i in range(len(self.capturedPoints)):
                self.capturedPoints[i] = self.projectPoint(
                    self.capturedPoints[i], distance, bearing)
            self.redrawRubberBand()
            self.redrawAreas()
            self.movingLineInitialPoint = currentPoint

    def projectPoint(self, point, distance, bearing):
        rads = bearing * pi / 180.0
        dx = distance * sin(rads)
        dy = distance * cos(rads)
        return QgsPointXY(point.x() + dx, point.y() + dy)

    def redrawAreas(self, mousePos=None):
        self.deleteLabels()

        if self.capturing and len(self.capturedPoints) > 0:
            for i in range(len(self.selectedFeatures)):
                geometry = QgsGeometry(self.selectedFeatures[i].geometry())
                movingPoints = list(self.capturedPoints)

                if mousePos != None:
                    movingPoints.append(
                        self.toLayerCoordinates(self.layer, mousePos))

                result, newGeometries, topoTestPoints = geometry.splitGeometry(
                    movingPoints, self.proj.topologicalEditing())

                self.addLabel(geometry)
                if newGeometries != None and len(newGeometries) > 0:
                    for i in range(len(newGeometries)):
                        self.addLabel(newGeometries[i])

    def addLabel(self, geometry):
        area = self.calculator.measureArea(geometry)
        labelPoint = geometry.pointOnSurface().vertexAt(0)
        label = QGraphicsTextItem("%.2f" % round(area, 2))
        label.setHtml(
            "<div style=\"color:#ffffff;background:#111111;padding:5px\">" +
            "%.2f" % round(area, 2) + " " +
            areaUnits[self.calculator.areaUnits()] + "</div>")
        point = self.toMapCoordinatesV2(self.layer, labelPoint)
        label.setPos(self.toCanvasCoordinates(QgsPointXY(point.x(),
                                                         point.y())))

        self.scene.addItem(label)
        self.labels.append(label)

    def deleteLabels(self):
        for i in range(len(self.labels)):
            self.scene.removeItem(self.labels[i])
        self.labels = []

    def canvasPressEvent(self, event):
        if self.movingVertices:
            for i in range(len(self.capturedPoints)):
                point = self.toMapCoordinates(self.layer,
                                              self.capturedPoints[i])
                currentVertex = self.toCanvasCoordinates(
                    QgsPointXY(point.x(), point.y()))
                if self.distancePoint(event.pos(),
                                      currentVertex) <= maxDistanceHitTest:
                    self.movingVertex = i
                    break

        if self.movingSegment:
            for i in range(len(self.capturedPoints) - 1):
                vertex1 = self.toMapCoordinates(self.layer,
                                                self.capturedPoints[i])
                currentVertex1 = self.toCanvasCoordinates(
                    QgsPointXY(vertex1.x(), vertex1.y()))
                vertex2 = self.toMapCoordinates(self.layer,
                                                self.capturedPoints[i + 1])
                currentVertex2 = self.toCanvasCoordinates(
                    QgsPointXY(vertex2.x(), vertex2.y()))
                if self.distancePointLine(
                        event.pos().x(),
                        event.pos().y(), currentVertex1.x(),
                        currentVertex1.y(), currentVertex2.x(),
                        currentVertex2.y()) <= maxDistanceHitTest:
                    self.movingSegm = i
                    break

        self.movingLineInitialPoint = self.toLayerCoordinates(
            self.layer, event.pos())

    def distancePoint(self, eventPos, vertexPos):
        return sqrt((eventPos.x() - vertexPos.x())**2 +
                    (eventPos.y() - vertexPos.y())**2)

    def canvasReleaseEvent(self, event):
        if self.movingVertices or self.movingSegment or self.movingLine:
            if event.button() == Qt.RightButton:
                self.finishOperation()
        elif self.addingVertices:
            if event.button() == Qt.LeftButton:
                self.addVertex(event.pos())
            elif event.button() == Qt.RightButton:
                self.finishOperation()
        elif self.removingVertices:
            if event.button() == Qt.LeftButton:
                self.removeVertex(event.pos())
            elif event.button() == Qt.RightButton:
                self.finishOperation()
        else:
            if event.button() == Qt.LeftButton:
                if not self.lineClosed:
                    if not self.capturing:
                        self.startCapturing()
                    self.addEndingVertex(event.pos())
            elif event.button() == Qt.RightButton:
                self.finishOperation()

        self.movingVertex = -1
        self.movingSegm = -1
        self.movingLineInitialPoint = None
        self.redrawActions()

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.stopCapturing()
        if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete:
            self.removeLastVertex()
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            self.finishOperation()

        event.accept()
        self.redrawActions()

    def finishOperation(self):
        self.doSplit()
        self.stopCapturing()
        self.initialize()
        self.startCapturing()

    def doSplit(self):
        if self.capturedPoints != None:
            self.layer.splitFeatures(self.capturedPoints,
                                     self.proj.topologicalEditing())

    def startCapturing(self):
        self.prepareRubberBand()
        self.prepareTempRubberBand()

        self.drawingLine = True
        self.capturing = True

        self.redrawActions()

    def prepareRubberBand(self):
        color = QColor("red")
        color.setAlphaF(0.78)

        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBand.setWidth(1)
        self.rubberBand.setColor(color)
        self.rubberBand.show()

    def prepareTempRubberBand(self):
        color = QColor("red")
        color.setAlphaF(0.78)

        self.tempRubberBand = QgsRubberBand(self.canvas,
                                            QgsWkbTypes.LineGeometry)
        self.tempRubberBand.setWidth(1)
        self.tempRubberBand.setColor(color)
        self.tempRubberBand.setLineStyle(Qt.DotLine)
        self.tempRubberBand.show()

    def redrawRubberBand(self):
        self.canvas.scene().removeItem(self.rubberBand)
        self.prepareRubberBand()
        for i in range(len(self.capturedPoints)):
            point = self.capturedPoints[i]
            if point.__class__ == QgsPoint:
                vertexCoord = self.toMapCoordinatesV2(self.layer,
                                                      self.capturedPoints[i])
                vertexCoord = QgsPointXY(vertexCoord.x(), vertexCoord.y())
            else:
                vertexCoord = self.toMapCoordinates(self.layer,
                                                    self.capturedPoints[i])

            self.rubberBand.addPoint(vertexCoord)

    def redrawTempRubberBand(self):
        if self.tempRubberBand != None:
            self.tempRubberBand.reset(QgsWkbTypes.LineGeometry)
            self.tempRubberBand.addPoint(
                self.toMapCoordinates(
                    self.layer,
                    self.capturedPoints[len(self.capturedPoints) - 1]))

    def stopCapturing(self):
        self.deleteLabels()
        self.deleteVertices()
        if self.rubberBand:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None
        self.drawingLine = False
        self.movingVertices = False
        self.showingVertices = False
        self.capturing = False
        self.capturedPoints = []
        self.canvas.refresh()

        self.redrawActions()

    def addEndingVertex(self, canvasPoint):
        mapPoint = self.toMapCoordinates(canvasPoint)
        layerPoint = self.toLayerCoordinates(self.layer, canvasPoint)

        self.rubberBand.addPoint(mapPoint)
        self.capturedPoints.append(layerPoint)

        self.tempRubberBand.reset(QgsWkbTypes.LineGeometry)
        self.tempRubberBand.addPoint(mapPoint)

    def removeLastVertex(self):
        if not self.capturing: return

        rubberBandSize = self.rubberBand.numberOfVertices()
        tempRubberBandSize = self.tempRubberBand.numberOfVertices()
        numPoints = len(self.capturedPoints)

        if rubberBandSize < 1 or numPoints < 1:
            return

        self.rubberBand.removePoint(-1)

        if rubberBandSize > 1:
            if tempRubberBandSize > 1:
                point = self.rubberBand.getPoint(0, rubberBandSize - 2)
                self.tempRubberBand.movePoint(tempRubberBandSize - 2, point)
        else:
            self.tempRubberBand.reset(self.bandType())

        del self.capturedPoints[-1]

    def addVertex(self, pos):
        newCapturedPoints = []
        for i in range(len(self.capturedPoints) - 1):
            newCapturedPoints.append(self.capturedPoints[i])
            vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i])
            currentVertex1 = self.toCanvasCoordinates(
                QgsPointXY(vertex1.x(), vertex1.y()))
            vertex2 = self.toMapCoordinates(self.layer,
                                            self.capturedPoints[i + 1])
            currentVertex2 = self.toCanvasCoordinates(
                QgsPointXY(vertex2.x(), vertex2.y()))

            distance = self.distancePointLine(pos.x(), pos.y(),
                                              currentVertex1.x(),
                                              currentVertex1.y(),
                                              currentVertex2.x(),
                                              currentVertex2.y())
            if distance <= maxDistanceHitTest:
                layerPoint = self.toLayerCoordinates(self.layer, pos)
                newCapturedPoints.append(layerPoint)

        newCapturedPoints.append(self.capturedPoints[len(self.capturedPoints) -
                                                     1])
        self.capturedPoints = newCapturedPoints

        self.redrawRubberBand()
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def removeVertex(self, pos):
        deletedFirst = False
        deletedLast = False
        newCapturedPoints = []
        for i in range(len(self.capturedPoints)):
            vertex = self.toMapCoordinates(self.layer, self.capturedPoints[i])
            currentVertex = self.toCanvasCoordinates(
                QgsPointXY(vertex.x(), vertex.y()))
            if not self.distancePoint(pos,
                                      currentVertex) <= maxDistanceHitTest:
                newCapturedPoints.append(self.capturedPoints[i])
            elif i == 0:
                deletedFirst = True
            elif i == len(self.capturedPoints) - 1:
                deletedLast = True

        self.capturedPoints = newCapturedPoints

        if deletedFirst and deletedLast:
            self.lineClosed = False

        self.redrawRubberBand()
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

        if len(self.capturedPoints) <= 2:
            self.stopRemovingVertices()

    def startMovingVertices(self):
        self.stopMovingLine()
        self.stopAddingVertices()
        self.stopRemovingVertices()
        self.stopMovingSegment()

        self.actionMoveVertices.setChecked(True)
        self.movingVertices = True
        self.showingVertices = True
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopMovingVertices(self):
        self.movingVertices = False
        self.actionMoveVertices.setChecked(False)
        self.restoreAction()

    def startAddingVertices(self):
        self.stopMovingVertices()
        self.stopRemovingVertices()
        self.stopMovingLine()
        self.stopMovingSegment()

        self.actionAddVertices.setChecked(True)
        self.addingVertices = True
        self.showingVertices = True
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopAddingVertices(self):
        self.addVertices = False
        self.actionAddVertices.setChecked(False)
        self.restoreAction()

    def startRemovingVertices(self):
        self.stopMovingVertices()
        self.stopAddingVertices()
        self.stopMovingLine()
        self.stopMovingSegment()

        self.actionRemoveVertices.setChecked(True)
        self.removingVertices = True
        self.showingVertices = True
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopRemovingVertices(self):
        self.removingVertices = False
        self.actionRemoveVertices.setChecked(False)
        self.restoreAction()

    def startMovingSegment(self):
        self.stopMovingVertices()
        self.stopMovingLine()
        self.stopAddingVertices()
        self.stopRemovingVertices()

        self.actionMoveSegment.setChecked(True)
        self.movingSegment = True
        self.showingVertices = False
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopMovingSegment(self):
        self.movingSegment = False
        self.actionMoveSegment.setChecked(False)
        self.restoreAction()

    def startMovingLine(self):
        self.stopMovingVertices()
        self.stopAddingVertices()
        self.stopRemovingVertices()
        self.stopMovingSegment()

        self.actionMoveLine.setChecked(True)
        self.movingLine = True
        self.showingVertices = False
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawAreas()
        self.redrawActions()

    def stopMovingLine(self):
        self.actionMoveLine.setChecked(False)
        self.restoreAction()

    def lineClose(self):
        self.lineClosed = True
        self.capturedPoints.append(self.capturedPoints[0])
        self.redrawRubberBand()
        self.redrawTempRubberBand()
        self.redrawAreas()
        self.redrawActions()

    def lineOpen(self):
        self.lineClosed = False
        del self.capturedPoints[-1]
        self.redrawRubberBand()
        self.redrawTempRubberBand()
        self.redrawAreas()
        self.redrawActions()

    def showVertices(self):
        for i in range(len(self.capturedPoints)):
            vertexc = self.toMapCoordinates(self.layer, self.capturedPoints[i])
            vertexCoords = self.toCanvasCoordinates(
                QgsPointXY(vertexc.x(), vertexc.y()))
            if i == self.movingVertex:
                vertex = self.scene.addRect(vertexCoords.x() - 5,
                                            vertexCoords.y() - 5, 10, 10,
                                            QPen(QColor("green")),
                                            QBrush(QColor("green")))
                self.vertices.append(vertex)
            elif i == len(self.capturedPoints
                          ) - 1 and self.movingVertex == 0 and self.lineClosed:
                vertex = self.scene.addRect(vertexCoords.x() - 5,
                                            vertexCoords.y() - 5, 10, 10,
                                            QPen(QColor("green")),
                                            QBrush(QColor("green")))
                self.vertices.append(vertex)
            else:
                vertex = self.scene.addRect(vertexCoords.x() - 4,
                                            vertexCoords.y() - 4, 8, 8,
                                            QPen(QColor("red")),
                                            QBrush(QColor("red")))
                self.vertices.append(vertex)

    def deleteVertices(self):
        for i in range(len(self.vertices)):
            self.scene.removeItem(self.vertices[i])
        self.vertices = []

    def lineMagnitude(self, x1, y1, x2, y2):
        return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2))

    def distancePointLine(self, px, py, x1, y1, x2, y2):
        magnitude = self.lineMagnitude(x1, y1, x2, y2)

        if magnitude < 0.00000001:
            distance = 9999
            return distance

        u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)))
        u = u1 / (magnitude * magnitude)

        if (u < 0.00001) or (u > 1):
            ix = self.lineMagnitude(px, py, x1, y1)
            iy = self.lineMagnitude(px, py, x2, y2)
            if ix > iy:
                distance = iy
            else:
                distance = ix
        else:
            ix = x1 + u * (x2 - x1)
            iy = y1 + u * (y2 - y1)
            distance = self.lineMagnitude(px, py, ix, iy)

        return distance

    def redrawVertices(self):
        self.deleteVertices()
        self.showVertices()

    def redrawActions(self):
        self.redrawActionMoveVertices()
        self.redrawActionAddVertices()
        self.redrawActionRemoveVertices()
        self.redrawActionMoveSegment()
        self.redrawActionLineClose()
        self.redrawActionLineOpen()
        self.redrawActionMoveLine()

    def redrawActionMoveVertices(self):
        self.actionMoveVertices.setEnabled(False)
        if len(self.capturedPoints) > 0:
            self.actionMoveVertices.setEnabled(True)

    def redrawActionAddVertices(self):
        self.actionAddVertices.setEnabled(False)
        if len(self.capturedPoints) >= 2:
            self.actionAddVertices.setEnabled(True)

    def redrawActionRemoveVertices(self):
        self.actionRemoveVertices.setEnabled(False)
        if len(self.capturedPoints) > 2:
            self.actionRemoveVertices.setEnabled(True)

    def redrawActionMoveSegment(self):
        self.actionMoveSegment.setEnabled(False)
        if len(self.capturedPoints) > 2:
            self.actionMoveSegment.setEnabled(True)

    def redrawActionLineClose(self):
        self.actionLineClose.setEnabled(False)
        if not self.lineClosed and len(self.capturedPoints) >= 3:
            self.actionLineClose.setEnabled(True)

    def redrawActionLineOpen(self):
        self.actionLineOpen.setEnabled(False)
        if self.lineClosed:
            self.actionLineOpen.setEnabled(True)

    def redrawActionMoveLine(self):
        self.actionMoveLine.setEnabled(False)
        if len(self.capturedPoints) > 0:
            self.actionMoveLine.setEnabled(True)
class VertexTracerTool(QgsMapTool):

    traceFound = pyqtSignal(QgsGeometry)

    def __init__(self, canvas):
        # some stuff we need from qgis
        QgsMapTool.__init__(self, canvas)
        self.canvas = canvas
        self.snapper = QgsSnappingUtils()
        # some stuff to control our state
        self.mCtrl = False
        self.started = False
        self.firstTimeOnSegment = True
        self.lastPointMustStay = False
        # some stuff to put our temp output
        self.lastPoint = None
        self.rb = None
        self.isPolygon = False

        # our own fancy cursor
        self.cursor = QCursor(
            QPixmap([
                "16 16 3 1", "      c None", ".     c #00FF00",
                "+     c #FFFFFF", "                ", "       +.+      ",
                "      ++.++     ", "     +.....+    ", "    +.     .+   ",
                "   +.   .   .+  ", "  +.    .    .+ ", " ++.    .    .++",
                " ... ...+... ...", " ++.    .    .++", "  +.    .    .+ ",
                "   +.   .   .+  ", "   ++.     .+   ", "    ++.....+    ",
                "      ++.++     ", "       +.+      "
            ]))

    # we need to know, if ctrl-key is pressed
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = True

    # and also if ctrl-key is released
    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = False
        # remove the last added point when the delete key is pressed
        if event.key() == Qt.Key_Backspace:
            self.rb.removeLastPoint()

    def canvasPressEvent(self, event):
        # on left click, we add a point
        if event.button() == 1:
            layer = self.canvas.currentLayer()
            # if it is the start of a new trace, set the rubberband up
            if not self.started:
                if layer.geometryType() == 1:
                    self.isPolygon = False
                if layer.geometryType() == 2:
                    self.isPolygon = True
                self.rb = QgsRubberBand(self.canvas, layer.geometryType())
                self.rb.setColor(QColor(255, 0, 0))
                # declare, that are we going to work, now!
            self.started = True
            if layer is not None:
                x = event.pos().x()
                y = event.pos().y()
                selPoint = QPoint(x, y)
                # try to get something snapped
                (retval,
                 result) = self.snapper.snapToBackgroundLayers(selPoint)

                # the point we want to have, is either from snapping result
                if result != []:
                    point = result[0].snappedVertex
                    # if we snapped something, it's either a vertex
                    if result[0].snappedVertexNr != -1:
                        self.firstTimeOnSegment = True
                    # or a point on a segment, so we have to declare, that a point on segment is found
                    else:
                        self.firstTimeOnSegment = False
                # or its some point from out in the wild
                else:
                    point = QgsMapToPixel.toMapCoordinates(
                        self.canvas.getCoordinateTransform(), x, y)
                    self.firstTimeOnSegment = True

                # bring the rubberband to the cursor i.e. the clicked point
                self.rb.movePoint(point)
                # and set a new point to go on with
                self.appendPoint(point)
                # try to remember that this point was on purpose i.e. clicked by the user
                self.lastPointMustStay = True
                self.firstTimeOnSegment = True

    def canvasMoveEvent(self, event):
        # QgsMessageLog.logMessage('fts: '+str(self.firstTimeOnSegment), 'state', 0)
        # QgsMessageLog.logMessage('lpc: '+str(self.lastPointMustStay), 'state', 0)
        if self.started:
            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            eventPoint = QPoint(x, y)
            # only if the ctrl key is pressed
            if self.mCtrl:
                layer = self.canvas.currentLayer()
                if layer is not None:
                    (retval,
                     result) = self.snapper.snapToBackgroundLayers(eventPoint)

                # so if we have found a snapping
                if result != []:
                    # pydevd.settrace()
                    # that's the snapped point
                    point = QgsPoint(result[0].snappedVertex)
                    # if it is a vertex, not a point on a segment
                    if result[0].snappedVertexNr != -1:
                        self.rb.movePoint(point)
                        self.appendPoint(point)
                        self.lastPointMustStay = True
                        # the next point found, may  be on a segment
                        self.firstTimeOnSegment = True
                    # we are on a segment
                    else:
                        self.rb.movePoint(point)
                        # if we are on a new segment, we add the point in any case
                        if self.firstTimeOnSegment:
                            self.appendPoint(point)
                            self.lastPointMustStay = True
                            self.firstTimeOnSegment = False
                        # if we are not on a new segemnt, we have to test, if this point is realy needed
                        else:
                            # but only if we have already enough points
                            # TODO: check if this is correct for lines also (they only need two points, to be vaild)
                            if self.rb.numberOfVertices() >= 3:
                                max = self.rb.numberOfVertices()
                                lastRbP = self.rb.getPoint(0, max - 2)
                                # QgsMessageLog.logMessage(str(self.rb.getPoint(0, max-1)), 'rb', 0)
                                nextToLastRbP = self.rb.getPoint(0, max - 3)
                                # QgsMessageLog.logMessage(str(self.rb.getPoint(0, max-2)), 'rb', 0)
                                # QgsMessageLog.logMessage(str(point), 'rb', 0)
                                if not self.pointOnLine(
                                        lastRbP, nextToLastRbP,
                                        QgsPoint(point)):
                                    self.appendPoint(point)
                                    self.lastPointMustStay = False
                                else:
                                    # TODO: schauen, ob der letzte punkt ein klick war, dann nicht löschen. entsrpechend auch die "punkt auf linie" neu berechenen.
                                    if not self.lastPointMustStay:
                                        self.rb.removeLastPoint()
                                        self.rb.movePoint(point)
                                        # QgsMessageLog.logMessage('point removed', 'click', 0)
                                # else:
                                # QgsMessageLog.logMessage('sleep', 'rb', 0)
                            else:
                                self.appendPoint(point)
                                self.lastPointMustStay = False
                                self.firstTimeOnSegment = False

                else:
                    # if nothing specials happens, just update the rubberband to the cursor position
                    point = QgsMapToPixel.toMapCoordinates(
                        self.canvas.getCoordinateTransform(), x, y)
                    self.rb.movePoint(point)

            else:
                # in "not-tracing" state, just update the rubberband to the cursor position
                # but we have still to snap to act like the "normal" digitize tool
                (retval,
                 result) = self.snapper.snapToBackgroundLayers(eventPoint)
                if result != []:
                    point = QgsPoint(result[0].snappedVertex)
                else:
                    point = QgsMapToPixel.toMapCoordinates(
                        self.canvas.getCoordinateTransform(), x, y)
                self.rb.movePoint(point)

    def canvasReleaseEvent(self, event):
        # with right click the digitizing is finished
        if event.button() == 2:

            layer = self.canvas.currentLayer()
            x = event.pos().x()
            y = event.pos().y()
            if layer is not None and self.started:
                selPoint = QPoint(x, y)
                (retval,
                 result) = self.snapper.snapToBackgroundLayers(selPoint)

                if result != []:
                    point = result[0].snappedVertex
                else:
                    point = QgsMapToPixel.toMapCoordinates(
                        self.canvas.getCoordinateTransform(), x, y)

                # add this last point
                self.appendPoint(QgsPoint(point))
                self.sendGeometry()

    def appendPoint(self, point):
        # don't add the point if it is identical to the last point we added
        if not (self.lastPoint == point):
            self.rb.addPoint(point)
            self.lastPoint = QgsPoint(point)
        else:
            pass

    # see: double QgsGeometryValidator::distLine2Point( QgsPoint p, QgsVector v, QgsPoint q )
    # distance of point q from line through p in direction v
    def pointOnLine(self, pntAft, pntBef, pntTest):
        p = QgsPoint(pntAft)
        vx = pntBef.x() - pntAft.x()
        vy = pntBef.y() - pntAft.y()
        vlength = math.sqrt(vy * vy + vx * vx)
        if vlength == 0:
            return False
        q = QgsPoint(pntTest)

        res = (vx * (q.y() - p.y()) - vy * (q.x() - p.x())) / vlength
        # res = 0 means point is on line, but we are not in a perfect world, so a tolerance is needed
        # to get rid of some numerical problems. Tolerance estimated by solid engenieering work (rule of thumb...)
        if res < 1E-10:
            return True
        else:
            return False

    def sendGeometry(self):
        layer = self.canvas.currentLayer()
        coords = []

        # backward compatiblity for a bug in qgsRubberband, that was fixed in 1.7
        if Qgis.QGIS_VERSION_INT >= 10700:
            [
                coords.append(self.rb.getPoint(0, i))
                for i in range(self.rb.numberOfVertices())
            ]
        else:
            [
                coords.append(self.rb.getPoint(0, i))
                for i in range(1, self.rb.numberOfVertices())
            ]

        # On the Fly reprojection, not necessary any more, mapToLayerCoordinates is clever enough on its own
        # layerEPSG = layer.srs().epsg()
        # projectEPSG = self.canvas.mapRenderer().destinationSrs().epsg()
        # if layerEPSG != projectEPSG:
        coords_tmp = coords[:]
        coords = []
        for point in coords_tmp:
            transformedPoint = self.canvas.mapRenderer().mapToLayerCoordinates(
                layer, point)
            coords.append(transformedPoint)

        coords_tmp = coords[:]
        coords = []
        lastPt = None
        for pt in coords_tmp:
            if (lastPt != pt):
                coords.append(pt)
                lastPt = pt

        # Add geometry to feature.
        if self.isPolygon:
            g = QgsGeometry().fromPolygon([coords])
        else:
            g = QgsGeometry().fromPolyline(coords)

        self.traceFound.emit(g)
        self.rb.reset(self.isPolygon)
        self.started = False

    def activate(self):
        self.canvas.setCursor(self.cursor)

    def deactivate(self):
        try:
            self.rb.reset()
        except AttributeError:
            pass

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True
class EditTool(QgsMapTool):
    wp_clicked = pyqtSignal(int)

    def __init__(self, mission_track, canvas, msglog):
        QgsMapTool.__init__(self, canvas)
        self.setCursor(Qt.CrossCursor)
        self.mission_track = mission_track
        self.msglog = msglog
        self.dragging = False
        self.feature = None
        self.vertex = None
        self.startcoord = None
        self.layer = self.mission_track.get_mission_layer()
        logger.info(self.mission_track.get_mission_name())

        self.rubber_band = QgsRubberBand(self.canvas(), QgsWkbTypes.LineGeometry)
        self.rubber_band.setWidth(2)
        self.rubber_band.setColor(QColor("green"))

        self.point_cursor_band = QgsRubberBand(self.canvas(), QgsWkbTypes.LineGeometry)
        self.point_cursor_band.setWidth(1)
        self.point_cursor_band.setLineStyle(Qt.DashLine)
        self.point_cursor_band.setColor(QColor(255, 0, 0, 100))

        self.mid_point_band = QgsRubberBand(self.canvas(), QgsWkbTypes.PointGeometry)
        self.mid_point_band.setColor(QColor(255, 0, 0, 100))
        self.mid_point_band.setIconSize(18)

        self.rubber_band_points = QgsRubberBand(self.canvas(), QgsWkbTypes.PointGeometry)
        self.rubber_band_points.setColor(QColor("green"))
        self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rubber_band_points.setIconSize(10)

        self.mission_track.mission_changed.connect(self.update_rubber_bands)

        self.vertex_marker = QgsVertexMarker(self.canvas())
        self.start_end_marker = StartEndMarker(canvas, self.mission_track.find_waypoints_in_mission(), QColor("green"))

        self.layer.startEditing()

        self.wp = []
        self.mCtrl = False
        # handler for mission feature
        self.update_rubber_bands(0)

        crs = canvas.mapSettings().destinationCrs()
        self.distance_calc = QgsDistanceArea()
        self.distance_calc.setSourceCrs(crs, QgsProject.instance().transformContext())
        self.distance_calc.setEllipsoid(crs.ellipsoidAcronym())

    def update_rubber_bands(self, current_wp):
        self.rubber_band.reset(QgsWkbTypes.LineGeometry)
        self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
        self.wp = self.mission_track.find_waypoints_in_mission()

        self.start_end_marker.update_markers(self.wp)

        if len(self.wp) > 0:

            for v in self.wp:
                pc = self.toLayerCoordinates(self.layer, QgsPointXY(v))
                self.rubber_band.addPoint(pc)
                self.rubber_band_points.addPoint(pc)
            logger.debug("MISSION UPDATE: now we have {} waypoints".format(len(self.wp)))

            self.vertex_marker.setCenter(QgsPointXY(self.wp[current_wp].x(), self.wp[current_wp].y()))
            self.vertex_marker.setColor(QColor(25, 255, 0))
            self.vertex_marker.setIconSize(7)
            self.vertex_marker.setIconType(QgsVertexMarker.ICON_X)  # ICON_BOX, ICON_CROSS, ICON_X
            self.vertex_marker.setPenWidth(2)
            self.vertex_marker.show()

            self.set_geometry()
        else:

            self.vertex_marker.hide()

    def set_control_state(self, state):
        self.mCtrl = state

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Control and not self.dragging:
            self.mCtrl = True
            pos = self.canvas().mouseLastXY()
            if not self.find_on_feature(pos, self.calc_tolerance()):
                self.show_dist_and_bearing_to_point()

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = False
            pos = self.canvas().mouseLastXY()
            if not self.find_on_feature(pos, self.calc_tolerance()) and not self.dragging:
                self.show_dist_and_bearing_to_point()
        return

    def canvasDoubleClickEvent(self, event):
        self.canvasPressEvent(event)

    def canvasPressEvent(self, event):
        if self.dragging:
            self.canvasReleaseEvent(event)

        map_pt, layer_pt = self.transform_coordinates(event.pos())
        tolerance = self.calc_tolerance()

        if not self.find_on_feature(event.pos(), tolerance):
            if event.button() == Qt.LeftButton:
                # we have clicked outside the track
                logger.debug("We have clicked outside the track")
                self.point_cursor_band.reset(QgsWkbTypes.LineGeometry)
                if not self.mCtrl:
                    # add step to mission at the end
                    self.mission_track.add_step(len(self.wp), layer_pt)
                    self.show_waypoint_distances(len(self.wp)-1)
                else:
                    self.mission_track.add_step(0, layer_pt)
                    self.show_waypoint_distances(0)
        else:

            logger.debug("We have clicked on the track")
            vertex = self.find_vertex_at(event.pos(), tolerance)

            if event.button() == Qt.LeftButton:
                if vertex is None:
                    logger.debug("We have clicked between vertexs")
                    # we have clicked in between vertex, add intermediate point
                    initial_vertex = self.find_segment_at(event.pos())
                    # self.mission_track.add_step(initial_vertex + 1, layerPt)

                    intersection = intersect_point_to_line(self.toLayerCoordinates(self.layer, event.pos()),
                                                           QgsPointXY(self.wp[initial_vertex]),
                                                           QgsPointXY(self.wp[initial_vertex + 1]))
                    logger.debug("intersection point: {} {}".format(str(intersection.x()), str(intersection.y())))
                    logger.debug("{} {} {} {}".format(self.wp[initial_vertex].x(), self.wp[initial_vertex].y(),
                                 self.wp[initial_vertex + 1].x(), self.wp[initial_vertex + 1].y()))
                    # layerPtIntersection = self.toLayerCoordinates(self.layer,intersection)
                    self.mission_track.add_step(initial_vertex + 1, intersection)
                    self.mid_point_band.reset(QgsWkbTypes.PointGeometry)
                    self.show_waypoint_distances(initial_vertex+1)
                else:
                    logger.debug("We have clicked on vertex {}".format(vertex))
                    # we have clicked on a vertex

                    # Left click -> move vertex.
                    self.dragging = True
                    self.vertex = vertex
                    self.startcoord = event.pos()
                    # self.moveVertexTo(layerPt)

            elif event.button() == Qt.RightButton:
                if vertex is not None and not self.dragging:
                    # Right click -> delete vertex.
                    self.delete_vertex(vertex)

                    if self.find_on_feature(event.pos(), tolerance):  # If cursor still over track
                        vertex = self.find_vertex_at(event.pos(), tolerance)
                        if vertex is None:  # Cursor is between vertexes
                            self.show_mid_point(event.pos())
                        else:  # Cursor is over a vertex
                            self.show_waypoint_distances(vertex)
                    else:
                        self.show_dist_and_bearing_to_point()

    def transform_coordinates(self, canvas_pt):
        return (self.toMapCoordinates(canvas_pt),
                self.toLayerCoordinates(self.layer, canvas_pt))

    def canvasMoveEvent(self, event):
        if self.dragging:
            self.move_vertex_to(self.toLayerCoordinates(self.layer, event.pos()))
            self.mission_track.hide_start_end_markers()
            self.vertex_marker.hide()
            self.start_end_marker.hide_markers()
            self.show_waypoint_distances(self.vertex)

        else:
            tolerance = self.calc_tolerance()
            if self.find_on_feature(event.pos(), tolerance):  # if mouse is over the track
                self.point_cursor_band.reset(QgsWkbTypes.LineGeometry)
                self.mid_point_band.reset(QgsWkbTypes.PointGeometry)
                vertex = self.find_vertex_at(event.pos(), tolerance)

                if vertex is None:  # Cursor is between vertexes
                    self.show_mid_point(event.pos())
                else:  # Cursor is over a vertex
                    self.show_waypoint_distances(vertex)

            else:
                self.mid_point_band.reset(QgsWkbTypes.PointGeometry)
                self.show_dist_and_bearing_to_point()

    def show_dist_and_bearing_to_point(self):
        """
        Finds distance and bearing from the last point (first if pressing ctrl) to the specified point and shows them
        in the message log. Also draws a line between the points.
        """
        bearing = 0.0

        self.point_cursor_band.reset(QgsWkbTypes.LineGeometry)
        point = self.canvas().mouseLastXY()
        if len(self.wp) > 0:
            cursor_point = self.toMapCoordinates(point)
            if self.mCtrl:
                anchor_point = QgsPointXY(self.wp[0])
            else:
                anchor_point = QgsPointXY(self.wp[len(self.wp) - 1])
            self.point_cursor_band.addPoint(cursor_point)
            self.point_cursor_band.addPoint(anchor_point)
            distance = self.distance_calc.measureLine([anchor_point, cursor_point])
            if distance != 0.0:
                bearing = self.distance_calc.bearing(anchor_point, cursor_point)
            self.msglog.logMessage("")
            if self.mCtrl:
                msg = "Distance to next point: "
            else:
                msg = "Distance to previous point: "
            self.msglog.logMessage(msg + "{:.3F} m.  Bearing: {:.3F} º.".format(distance, math.degrees(bearing)),
                                   "Distance and bearing", 0)
        else:
            self.msglog.logMessage("")

    def show_mid_point(self, cursor):
        """
        Finds the projection of the cursor over the track and draws a circle in that point.
        Finds the distances between this projection point and the previous and next points in the mission
        and shows them in the message log.
        :param cursor: position to be projected over the track
        """
        prev_vertex = self.find_segment_at(cursor)
        prev_point = QgsPointXY(self.wp[prev_vertex])
        next_point = QgsPointXY(self.wp[prev_vertex + 1])
        cursor_point = self.toMapCoordinates(cursor)
        intersection = intersect_point_to_line(cursor_point, prev_point, next_point)
        self.mid_point_band.addPoint(intersection)
        distance1 = self.distance_calc.measureLine([prev_point, intersection])
        distance2 = self.distance_calc.measureLine([intersection, next_point])
        self.msglog.logMessage("")
        self.msglog.logMessage("Distance to previous point: {:.3F} m.  Distance to next point: {:.3F} m."
                               .format(distance1, distance2), "Distance between points", 0)

    def show_waypoint_distances(self, vertex):
        """
        Finds the distances to adjacent waypoints of vertex and shows them in the message log
        :param vertex: index of the waypoint from the mission
        """
        curr_point = self.rubber_band_points.getPoint(QgsWkbTypes.PointGeometry, vertex)
        if vertex == 0:
            if len(self.wp) > 1:
                next_point = QgsPointXY(self.wp[vertex+1])
                distance = self.distance_calc.measureLine([curr_point, next_point])
                bearing = self.distance_calc.bearing(next_point, curr_point)
                msg = "Distance to next point: {:.3F} m.  Bearing: {:.3F} º.".format(distance, math.degrees(bearing))
            else:
                msg = ""
            self.msglog.logMessage("")
            self.msglog.logMessage(msg+" (Waypoint {}) ".format(vertex+1), "Vertex distances", 0)
        elif vertex == len(self.wp) - 1:
            prev_point = QgsPointXY(self.wp[vertex-1])
            distance = self.distance_calc.measureLine([prev_point, curr_point])
            bearing = self.distance_calc.bearing(prev_point, curr_point)
            msg = "Distance to previous point: {:.3F} m.  Bearing: {:.3F} º.".format(distance, math.degrees(bearing))
            self.msglog.logMessage("")
            self.msglog.logMessage(msg+" (Waypoint {})".format(vertex+1), "Vertex distances", 0)
        else:
            prev_point = QgsPointXY(self.wp[vertex-1])
            next_point = QgsPointXY(self.wp[vertex+1])
            distance1 = self.distance_calc.measureLine(prev_point, curr_point)
            distance2 = self.distance_calc.measureLine(curr_point, next_point)
            msg = "Distance to previous point: {:.3F} m.  Distance to next point: {:.3F} m."\
                .format(distance1, distance2)
            self.msglog.logMessage("")
            self.msglog.logMessage(msg+" (Waypoint {})".format(vertex+1), "Vertex distances", 0)

    def hide_point_cursor_band(self):
        """
        Hides the rubber band drawn from last (or first) point to cursor
        """
        self.point_cursor_band.reset(QgsWkbTypes.LineGeometry)

    def canvasReleaseEvent(self, event):
        if self.dragging and event.button() == Qt.LeftButton:
            self.dragging = False
            self.vertex_marker.show()
            mapPt, layerPt = self.transform_coordinates(event.pos())
            # Check distance with initial point
            dist = math.sqrt(
                (self.startcoord.x() - event.pos().x()) ** 2 + (self.startcoord.y() - event.pos().y()) ** 2)
            tolerance = self.calc_tolerance()
            if dist > tolerance:
                self.move_vertex_to(layerPt)
                self.mission_track.change_position(self.vertex, layerPt)
                self.wp_clicked.emit(self.vertex)
                self.feature = None
                self.vertex = None
                self.layer.updateExtents()
            else:
                # If release point is the same, has been just a click
                self.move_vertex_to(self.toLayerCoordinates(self.layer, QgsPointXY(self.wp[self.vertex])))
                self.wp_clicked.emit(self.vertex)
                self.feature = None
                self.vertex = None

    def calc_tolerance(self):
        """
        Compute the tolerance on canvas

        :return: tolerance
        """
        # 2% of tolerance
        width_tolerance = 0.02 * self.canvas().width()
        height_tolerance =  0.02 * self.canvas().height()
        if width_tolerance < height_tolerance:
            tolerance = width_tolerance
        else:
            tolerance = height_tolerance
        return tolerance

    def move_vertex_to(self, layerPt):
        """
        Move current vertex to layerPt position.

        :param layerPt: layer point
        :type: QgsPointXY
        """
        if len(self.wp) > 1:
            self.rubber_band.movePoint(self.vertex, layerPt)
            self.rubber_band_points.movePoint(self.vertex, layerPt)
        elif len(self.wp) == 1:
            # A rubber band with PointGeometry and only 1 point acts as if it had 2 points, we need to reset it in
            # order to move the point.
            self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
            self.rubber_band_points.addPoint(layerPt)


    def delete_vertex(self, vertex):
        """
        Delete step 'vertex'.
        :param vertex: step

        """
        self.mission_track.remove_step(vertex)
        self.dragging = False
        self.vertex = None

    def find_on_feature(self, pos, tolerance):
        """
        if clicked point has some segment at a smaller distance than tolerance means that we've clicked on the track

        :param pos: The point that we've clicked
        :param tolerance: The tolerance of pos
        :return: bool
        """
        if len(self.wp) > 1:
            dist_to_segment = []
            for v in range(0, len(self.wp) - 1):
                # convert layer coordinates to canvas coordinates
                a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v]))
                b1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v + 1]))

                dist_to_segment.append(
                    self.dist_to_segment(a1.x(),
                                         a1.y(),
                                         b1.x(),
                                         b1.y(),
                                         pos.x(),
                                         pos.y()))
                logger.debug("dist to segment: {}".format(dist_to_segment))
                if dist_to_segment[v] < tolerance:
                    return True

            return False
        else:
            # last waypoint
            vertex = self.find_vertex_at(pos, tolerance)
            if vertex is None:
                return False
            else:
                return True

    def find_segment_at(self, pos):
        """
        get the segment that is closer to the clicked point and return its initial vertex

        :param pos: the point that we've clicked
        :return: initial vertex of the segment
        """
        dist_to_segment = []
        for v in range(0, len(self.wp) - 1):
            a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v]))
            b1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v + 1]))
            dist_to_segment.append(self.dist_to_segment(a1.x(),
                                                        a1.y(),
                                                        b1.x(),
                                                        b1.y(),
                                                        pos.x(),
                                                        pos.y()))

        vertex = dist_to_segment.index(min(dist_to_segment))
        return vertex

    def find_vertex_at(self, pos, tolerance):
        """
        get the vertex that is closer to the clicked point


        :param pos: The point that we've clicked
        :param tolerance: The tolerance of pos
        :return: vertex or None
        """
        if len(self.wp) > 0:
            dist_to_vertex = []
            logger.debug("tolerance {}".format(tolerance))
            for v in range(0, len(self.wp)):
                a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v]))
                dist_to_vertex.append(math.sqrt((pos.x() - a1.x()) ** 2 + (pos.y() - a1.y()) ** 2))
                logger.debug("dist to vertex: {}".format(dist_to_vertex))

            vertex = dist_to_vertex.index(min(dist_to_vertex))
            if min(dist_to_vertex) > tolerance:
                return None
            else:
                logger.debug("ON VERTEX")
                return vertex
        else:
            return None

    def dist_to_segment(self, ax, ay, bx, by, cx, cy):
        """
        Computes the minimum distance between a point (cx, cy) and a line segment with endpoints (ax, ay) and (bx, by).
        :param ax: endpoint 1, x-coordinate
        :param ay: endpoint 1, y-coordinate
        :param bx: endpoint 2, x-coordinate
        :param by: endpoint 2, y-coordinate
        :param cx: point, x-coordinate
        :param cy: point, x-coordinate
        :return: minimum distance between point and line segment
        """
        # calculate tolerance
        tolerance = self.calc_tolerance()
        # get distance between points c-a and c-b
        dist_to_a = math.sqrt((cx - ax) ** 2 + (cy - ay) ** 2)
        dist_to_b = math.sqrt((cx - bx) ** 2 + (cy - by) ** 2)
        # if distance to point a or distance to point b is smaller than tolerance, return -1
        if (dist_to_a < tolerance) or (dist_to_b < tolerance):
            return -1

        # if one coordinate are between a coordinates or b coordinates
        if is_between(ax, ay, bx, by, cx, cy):

            y = (by - ay)
            x = (bx - ax)

            # line defined by two points formula
            num = abs((y * cx) - (x * cy) + (bx * ay) - (by * ax))
            den = math.sqrt(y ** 2 + x ** 2)
            dl = num / den
            return dl

        else:
            return tolerance + 1

    def set_geometry(self):
        """
        Save rubber band to geometry of the layer
        """
        if self.layer.featureCount() == 0:
            # no feature yet created
            f = QgsFeature()
            if len(self.wp) == 1:
                f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(self.wp[0].x(), self.wp[0].y())))
            else:
                f.setGeometry(QgsGeometry.fromPolyline(self.wp))
            # self.layer.dataProvider().addFeatures([f])
            self.layer.addFeatures([f])
        else:
            # mission feature present, edit geometry
            feats = self.layer.getFeatures()
            for f in feats:
                if len(self.wp) == 1:
                    self.layer.changeGeometry(f.id(),
                                              QgsGeometry.fromPointXY(QgsPointXY(self.wp[0].x(), self.wp[0].y())))
                else:
                    self.layer.changeGeometry(f.id(), QgsGeometry.fromPolyline(self.wp))
        self.layer.commitChanges()
        self.layer.startEditing()

    def close_band(self):
        self.start_end_marker.close_markers()
        self.vertex_marker.hide()
        self.canvas().scene().removeItem(self.vertex_marker)
        self.vertex_marker = None
        self.mission_track.mission_changed.disconnect()
        self.layer.commitChanges()
        self.rubber_band.hide()
        self.mid_point_band.hide()
        self.rubber_band_points.hide()
        self.point_cursor_band.hide()
        self.canvas().scene().removeItem(self.rubber_band)
        self.canvas().scene().removeItem(self.mid_point_band)
        self.canvas().scene().removeItem(self.rubber_band_points)
        self.canvas().scene().removeItem(self.point_cursor_band)
        self.rubber_band = None
        self.mid_point_band = None
        self.rubber_band_points = None
        self.point_cursor_band = None
        self.msglog.logMessage("")
class APISMapToolEmitPolygonAndPoint(QgsMapTool, APISMapToolMixin):

    mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry,
                                 QgsCoordinateReferenceSystem)

    def __init__(self, canvas):
        QgsMapTool.__init__(self, canvas)

        self.canvas = canvas
        self.rubberBand = None
        self.tempRubberBand = None
        self.vertexMarker = None
        self.capturedPoints = []
        self.derivedPoint = None
        self.capturing = False
        self.setCursor(Qt.CrossCursor)

    def canvasReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if not self.capturing:
                self.startCapturing()
            self.addVertex(event.pos())
        elif event.button() == Qt.RightButton:
            point = self.getDerivedPoint()
            polygon = self.getCapturedPolygon()
            self.stopCapturing()
            if point is not None and polygon is not None:
                pointGeom = self.getPointGeometry(point)
                polygonGeom = self.getPolygonGeometry(polygon)
                if pointGeom is not None and polygonGeom is not None:
                    self.mappingFinished.emit(
                        pointGeom, polygonGeom,
                        self.canvas.mapSettings().destinationCrs())
                else:
                    self.clearScene()
            else:
                self.clearScene()

    def canvasMoveEvent(self, event):
        if self.tempRubberBand is not None and self.capturing:
            mapPt = self.transformCoordinates(event.pos())
            self.tempRubberBand.movePoint(mapPt)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete:
            self.removeLastVertex()
            event.ignore()
        if event.key() == Qt.Key_Escape:
            self.stopCapturing()
            self.clearScene()
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            point = self.getDerivedPoint()
            polygon = self.getCapturedPolygon()
            self.stopCapturing()
            if point is not None and polygon is not None:
                pointGeom = self.getPointGeometry(point)
                polygonGeom = self.getPolygonGeometry(polygon)
                if pointGeom is not None and polygonGeom is not None:
                    self.mappingFinished.emit(
                        pointGeom, polygonGeom,
                        self.canvas.mapSettings().destinationCrs())
                else:
                    self.clearScene()
            else:
                self.clearScene()

    def startCapturing(self):
        self.clearScene()

        self.rubberBand = QgsRubberBand(self.canvas,
                                        QgsWkbTypes.PolygonGeometry)
        self.rubberBand.setWidth(2)
        self.rubberBand.setFillColor(QColor(220, 0, 0, 120))
        self.rubberBand.setStrokeColor(QColor(220, 0, 0))
        self.rubberBand.setLineStyle(Qt.DotLine)
        self.rubberBand.show()

        self.tempRubberBand = QgsRubberBand(self.canvas,
                                            QgsWkbTypes.PolygonGeometry)
        self.tempRubberBand.setWidth(2)
        self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0))
        self.tempRubberBand.setStrokeColor(QColor(220, 0, 0))
        self.tempRubberBand.setLineStyle(Qt.DotLine)
        self.tempRubberBand.show()

        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setIconType(1)
        self.vertexMarker.setColor(QColor(220, 0, 0))
        self.vertexMarker.setIconSize(16)
        self.vertexMarker.setPenWidth(3)
        self.vertexMarker.show()

        self.capturing = True

    def clearScene(self):
        if self.vertexMarker:
            self.canvas.scene().removeItem(self.vertexMarker)
            self.vertexMarker = None
        if self.rubberBand:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None

    def stopCapturing(self):
        if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices(
        ) < 3:
            self.canvas.scene().removeItem(self.vertexMarker)
            self.vertexMarker = None
        if self.rubberBand and self.rubberBand.numberOfVertices() < 3:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None
        self.capturing = False
        self.capturedPoints = []
        self.derivedPoint = None
        self.canvas.refresh()

    def addVertex(self, canvasPoint):
        mapPt = self.transformCoordinates(canvasPoint)

        self.rubberBand.addPoint(mapPt)
        self.capturedPoints.append(mapPt)

        bandSize = self.rubberBand.numberOfVertices()
        if bandSize > 2:

            rubGeom = self.rubberBand.asGeometry()
            cpGeom = rubGeom.centroid()
            if not rubGeom.contains(cpGeom):
                cpGeom = rubGeom.pointOnSurface()
            #nearestCp = rubGeom.nearestPoint(cpGeom)
            self.vertexMarker.setCenter(cpGeom.asPoint())
            self.derivedPoint = cpGeom.asPoint()
            self.vertexMarker.show()

        self.tempRubberBand.reset(QgsWkbTypes.PolygonGeometry)
        firstPoint = self.rubberBand.getPoint(0, 0)
        self.tempRubberBand.addPoint(firstPoint)
        self.tempRubberBand.movePoint(mapPt)
        self.tempRubberBand.addPoint(mapPt)

    def removeLastVertex(self):
        if not self.capturing:
            return

        bandSize = self.rubberBand.numberOfVertices()
        tempBandSize = self.tempRubberBand.numberOfVertices()
        numPoints = len(self.capturedPoints)

        if bandSize < 1 or numPoints < 1:
            return

        self.rubberBand.removePoint(-1)

        if bandSize > 1:
            if tempBandSize > 1:
                point = self.rubberBand.getPoint(0, bandSize - 2)
                self.tempRubberBand.movePoint(tempBandSize - 2, point)
        else:
            self.tempRubberBand.reset(QgsWkbTypes.PolygonGeometry)

        bandSize = self.rubberBand.numberOfVertices()
        if bandSize < 3:
            self.vertexMarker.hide()
        else:
            rubGeom = self.rubberBand.asGeometry()
            cpGeom = rubGeom.centroid()
            if not rubGeom.contains(cpGeom):
                cpGeom = rubGeom.pointOnSurface()
            #nearestCp = rubGeom.nearestPoint(cpGeom)
            self.vertexMarker.setCenter(cpGeom.asPoint())
            self.derivedPoint = cpGeom.asPoint()
            self.vertexMarker.show()

        del self.capturedPoints[-1]

    def getCapturedPolygon(self):
        polygon = self.capturedPoints

        if len(polygon) < 3:
            return None
        else:
            return polygon

    def getDerivedPoint(self):
        point = self.derivedPoint

        if point is None:
            return None
        else:
            return point

    def getPointGeometry(self, geom):
        p = QgsGeometry.fromPointXY(geom)
        if p.isGeosValid() and not p.isEmpty():
            return p
        else:
            return None

    def getPolygonGeometry(self, geom):
        p = QgsGeometry.fromPolygonXY([geom])
        if p.isGeosValid() and not p.isEmpty():
            return p
        else:
            return None
Example #5
0
class SignatureTool(QgsMapTool):
    """
        On Double click it will callback with an array with a single pixel.
        On Single click without releasing it will draw a square and callback
        the starting and ending point in an array.
        On Single click with releasing it will start drawing a polygon and
        every subsequent single click will add a new vertex in the polygon.
        On Right click it will callback with an array with all the vertex in
        the polygon.
        On Escape it will clean up the array and start over.
    """
    def __init__(self, canvas, layer, callback):
        QgsMapTool.__init__(self, canvas)
        self._canvas = canvas
        self._layer = layer
        self._callback = callback
        self._pixels = []
        self._start_point = None
        self._mode = Mode.NONE
        self._rubber_band = QgsRubberBand(self._canvas)
        self._rubber_band.setColor(Qt.red)
        self._rubber_band.setWidth(1)
        self.parent().setCursor(Qt.CrossCursor)

    def getPoint(self, pos):
        x = pos.x()
        y = pos.y()
        return self._canvas.getCoordinateTransform().toMapCoordinates(x, y)

    def getRowCol(self, point):
        # clicked position on screen to map coordinates
        data_provider = self._layer.dataProvider()
        extent = data_provider.extent()
        width = data_provider.xSize() if data_provider.capabilities() \
            & data_provider.Size else 1000
        height = data_provider.ySize() if data_provider.capabilities() \
            & data_provider.Size else 1000
        xres = extent.width() / width
        yres = extent.height() / height
        if extent.xMinimum() <= point.x() <= extent.xMaximum() and \
            extent.yMinimum() <= point.y() <= extent.yMaximum():
            col = int(floor((point.x() - extent.xMinimum()) / xres))
            row = int(floor((extent.yMaximum() - point.y()) / yres))
            return (row, col)
        else:
            return None

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self._pixels = []
            self.finish()

    def canvasMoveEvent(self, event):
        point = self.getPoint(event.pos())
        if self._mode is Mode.SQUARE:
            if not point.compare(self._start_point):
                self._rubber_band.reset()
                self._rubber_band.addPoint(self._start_point, False)
                self._rubber_band.addPoint(
                    QgsPointXY(self._start_point.x(), point.y()), False)
                self._rubber_band.addPoint(point, False)
                self._rubber_band.addPoint(
                    QgsPointXY(point.x(), self._start_point.y()), False)
                self._rubber_band.closePoints()
        elif self._mode is Mode.POLYGON:
            self._rubber_band.movePoint(
                self._rubber_band.numberOfVertices() - 1, point)

    def canvasReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            point = self.getPoint(event.pos())
            if self._mode is Mode.SQUARE:
                if self._start_point.compare(point):
                    self._mode = Mode.POLYGON
                    self._rubber_band.addPoint(point)
                    self._start_point = None
                else:
                    self._pixels = []
                    # The last vertex is repeated
                    for i in range(self._rubber_band.numberOfVertices() - 1):
                        self._pixels.append(
                            self.getRowCol(self._rubber_band.getPoint(0, i)))
                    self.finish()
            elif self._mode is Mode.POLYGON:
                self._rubber_band.addPoint(point)

    def canvasPressEvent(self, event):
        if event.button() == Qt.LeftButton:
            point = self.getPoint(event.pos())
            pixel = self.getRowCol(point)
            if self._mode is Mode.NONE:
                self._mode = Mode.SQUARE
                self._start_point = QgsPointXY(point.x(), point.y())
            elif self._mode is Mode.POLYGON:
                self._rubber_band.removePoint(
                    self._rubber_band.numberOfVertices() - 1)
            else:
                self._mode = Mode.POLYGON
            if pixel:
                self._rubber_band.addPoint(point)
                self._pixels.append(pixel)
        elif event.button() == Qt.RightButton:
            self.finish()

    def finish(self):
        self._canvas.unsetMapTool(self)
        self._rubber_band.reset()
        self._mode = Mode.NONE
        self._start_point = None
        if len(self._pixels) > 0:
            self._callback(self._layer.hiperqube_id(), self._pixels)
Example #6
0
class LineMapTool(QgsMapTool):

    def __init__(self, iface, settings, action, index_action):  
        ''' Class constructor '''
        
        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.settings = settings
        self.index_action = index_action
        self.elem_type_type = self.settings.value('insert_values/'+str(index_action)+'_elem_type_type')           
        QgsMapTool.__init__(self, self.canvas)
        self.setAction(action)

        # Set rubber band features
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Line)
        mFillColor = QColor(255, 0, 0);
        self.rubberBand.setColor(mFillColor)
        self.rubberBand.setWidth(2)
        self.reset()        
        
        # Vertex marker
        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setColor(QColor(0, 255, 0))
        self.vertexMarker.setIconSize(9)
        self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X
        self.vertexMarker.setPenWidth(5)

        # Snapper
        self.snapper = QgsMapCanvasSnapper(self.canvas)

        # Control button state
        self.mCtrl = False
        self.started = False
        
        # Tracing options
        self.firstTimeOnSegment = True
        self.lastPointMustStay = False
        self.lastPoint = None
        
        # Change map tool cursor
        self.cursor = QCursor()
        self.cursor.setShape(Qt.CrossCursor)
        self.parent().setCursor(self.cursor)                                             
 
 
    def keyPressEvent(self,  event):
        ''' We need to know, if ctrl-key is pressed '''
        if event.key() == Qt.Key_Control:
            self.mCtrl = True


    def keyReleaseEvent(self,  event):
        ''' Ctrl-key is released '''
        if event.key() == Qt.Key_Control:
            self.mCtrl = False
        
        # Remove the last added point when the delete key is pressed
        if event.key() == Qt.Key_Backspace:
            self.rubberBand.removeLastPoint()
       

    def reset(self):
        self.start_point = self.end_point = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QGis.Line)
                       
    
    
    ''' QgsMapTools inherited event functions '''
        
    def canvasPressEvent(self, event):
        ''' On left click, we add a point '''
        
        if event.button()  ==  1:
            # layer = self.canvas.currentLayer()
            layer = self.iface.activeLayer() 
    
            # Declare, that are we going to work
            self.started = True
            
            if layer <> None:
                x = event.pos().x()
                y = event.pos().y()
                selPoint = QPoint(x,y)
    
                # Check something snapped
                (retval,result) = self.snapper.snapToBackgroundLayers(selPoint) #@UnusedVariable
              
                # The point we want to have, is either from snapping result
                if result <> []:
                    point = result[0].snappedVertex
    
                    # If we snapped something, it's either a vertex
                    if result[0].snappedVertexNr <> -1:
                        self.firstTimeOnSegment = True
                
                    # Or a point on a segment, so we have to declare, that a point on segment is found
                    else:
                        self.firstTimeOnSegment = False
    
                # Or its some point from out in the wild
                else:
                    point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y)
                    self.firstTimeOnSegment = True
    
                # Bring the rubberband to the cursor i.e. the clicked point
                self.rubberBand.movePoint(point)
    
                # Set a new point to go on with
                self.appendPoint(point)
              
                # Try to remember that this point was on purpose i.e. clicked by the user
                self.lastPointMustStay = True
                self.firstTimeOnSegment = True    
                       
    
    def canvasMoveEvent(self, event):
        
        # Hide highlight
        self.vertexMarker.hide()
            
        # Get the click
        x = event.pos().x()
        y = event.pos().y()
        eventPoint = QPoint(x,y)

        # Snapping        
        (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint)   #@UnusedVariable

        # That's the snapped point
        if result <> []:
            point = QgsPoint(result[0].snappedVertex)

            # Add marker    
            self.vertexMarker.setCenter(point)
            self.vertexMarker.show()
            
        # Check tracing 
        if self.started:
                        
            # Only if the ctrl key is pressed
            if self.mCtrl == True:
                 
                # So if we have found a snapping
                if result <> []:

                    # If it is a vertex, not a point on a segment
                    if result[0].snappedVertexNr <> -1: 
                        self.rubberBand.movePoint(point) 
                        self.appendPoint(point)
                        self.lastPointMustStay = True
                    
                        # The next point found, may  be on a segment
                        self.firstTimeOnSegment = True
                                          
                    # We are on a segment
                    else:
                        self.rubberBand.movePoint(point)
                    
                        # If we are on a new segment, we add the point in any case
                        if self.firstTimeOnSegment:
                            self.appendPoint(point)
                            self.lastPointMustStay = True
                            self.firstTimeOnSegment = False
                    
                        # if we are not on a new segment, we have to test, if this point is really needed
                        else:
                            # but only if we have already enough points
                            if self.rubberBand.numberOfVertices() >=3:
                                num_vertexs = self.rubberBand.numberOfVertices()    
                                lastRbP = self.rubberBand.getPoint(0, num_vertexs-2)
                                nextToLastRbP = self.rubberBand.getPoint(0, num_vertexs-3)                          
                                if not self.pointOnLine(lastRbP, nextToLastRbP, QgsPoint(point)):
                                    self.appendPoint(point)
                                    self.lastPointMustStay = False
                                else:
                                    if not self.lastPointMustStay:
                                        self.rubberBand.removeLastPoint()
                                        self.rubberBand.movePoint(point)
                            else:
                                self.appendPoint(point)
                                self.lastPointMustStay = False
                                self.firstTimeOnSegment = False
          
                else:
                    #if nothing specials happens, just update the rubberband to the cursor position
                    point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform (), x, y)
                    self.rubberBand.movePoint(point)
          
            else:
                ''' In "not-tracing" state, just update the rubberband to the cursor position
                 but we have still to snap to act like the "normal" digitize tool '''
                if result <> []:
                    point = QgsPoint(result[0].snappedVertex)
                
                    # Add marker    
                    self.vertexMarker.setCenter(point)
                    self.vertexMarker.show()

                else:
                    point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(),  x, y)
                
                self.rubberBand.movePoint(point)            
        
        
    def canvasReleaseEvent(self, event):
        ''' With right click the digitizing is finished '''
        
        if event.button() == 2:
      
            # layer = self.canvas.currentLayer()
            layer = self.iface.activeLayer()
            
            x = event.pos().x()
            y = event.pos().y()
            
            if layer <> None and self.started == True: 
                selPoint = QPoint(x,y)
                (retval,result) = self.snapper.snapToBackgroundLayers(selPoint) #@UnusedVariable
        
            if result <> []:
                point = result[0].snappedVertex #@UnusedVariable
            else:
                point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y)  #@UnusedVariable
        
            self.sendGeometry()
 
        
    def appendPoint(self, point):
        ''' Don't add the point if it is identical to the last point we added '''
        
        if not (self.lastPoint == point) :      
            self.rubberBand.addPoint(point)
            self.lastPoint = QgsPoint(point)
        else:
            pass
          
    
    def sendGeometry(self):
        
        #layer = self.canvas.currentLayer()
        layer = self.iface.activeLayer()        
        
        coords = []
        self.rubberBand.removeLastPoint()
        
        if QGis.QGIS_VERSION_INT >= 10700:
            [coords.append(self.rubberBand.getPoint(0, i)) for i in range(self.rubberBand.numberOfVertices())]
        else:
            [coords.append(self.rubberBand.getPoint(0,i)) for i in range(1,self.rubberBand.numberOfVertices())]
        
        # On the Fly reprojection, not necessary any more, mapToLayerCoordinates is clever enough on its own
        #layerEPSG = layer.srs().epsg()
        #projectEPSG = self.canvas.mapRenderer().destinationSrs().epsg()
        #if layerEPSG != projectEPSG:
        coords_tmp = coords[:]
        coords = []
        for point in coords_tmp:
            transformedPoint = self.canvas.mapRenderer().mapToLayerCoordinates( layer, point );
            coords.append(transformedPoint)

        # Filter duplicated points
        coords_tmp = coords[:]
        coords = []
        lastPt = None
        for pt in coords_tmp:
            if (lastPt <> pt) :
                coords.append(pt)
                lastPt = pt
                 
        # Add geometry to feature.
        g = QgsGeometry().fromPolyline(coords)

        self.rubberBand.reset(QGis.Line)
        self.started = False
        
        # Write the feature
        self.createFeature(g)
        
        
    def createFeature(self, geom):

        # layer = self.canvas.currentLayer()
        layer = self.iface.activeLayer()        
        provider = layer.dataProvider()
        f = QgsFeature()
    	
        if (geom.isGeosValid()):
            f.setGeometry(geom)
        else:
            reply = QMessageBox.question(self.iface.mainWindow(), 'Feature not valid',
                "The geometry of the feature you just added isn't valid. Do you want to use it anyway?",
            QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                f.setGeometry(geom)
            else:
                return False
				
      
        # Add attribute fields to feature.
        fields = layer.pendingFields()
        
        try: #API-Break 1.8 vs. 2.0 handling
            attr = f.initAttributes(len(fields))    #@UnusedVariable
            for i in range(len(fields)):
                f.setAttribute(i, provider.defaultValue(i))
              
        except AttributeError: #<=1.8
            # Add attributefields to feature.
            for i in fields:
                f.addAttribute(i, provider.defaultValue(i))
        
        idx = layer.fieldNameIndex('epa_type')
        f[idx] = self.elem_type_type
        
        # Upload changes
        layer.startEditing()
        layer.addFeature(f)
        
        # Control PostgreSQL exceptions
        boolOk = layer.commitChanges()

        # Update canvas        
        self.canvas.refresh()
 
        # Capture edit exception
        if boolOk:
                        
            # Spatial query to retrieve last added line, start searchingcandidates
            cands = layer.getFeatures(QgsFeatureRequest().setFilterRect(f.geometry().boundingBox()))

            # Iterate on candidates
            for line_feature in cands:
                if line_feature.geometry().equals(f.geometry()):

                    # Highlight
                    layer.setSelectedFeatures([line_feature.id()])

                    # Open form
                    self.iface.openFeatureForm(layer, line_feature)
                    break
        
        else:
            
            # Delete
            layer.rollBack()
            
            # User error
            msg = "Error adding PIPE: Typically this occurs if\n the first point is not located in a existing\n node or feature is out of the defined sectors."
            QMessageBox.information(None, "PostgreSQL error:", msg)

                
    def activate(self):
        self.canvas.setCursor(self.cursor)
  
  
    def deactivate(self):
        try:
            self.rubberBand.reset(QGis.Line)
        except AttributeError:
            pass
class ApisMapToolEmitPolygonAndPoint(QgsMapTool, ApisMapToolMixin):

    mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem)

    def __init__(self, canvas):
        QgsMapTool.__init__(self, canvas)

        self.canvas = canvas
        self.rubberBand = None
        self.tempRubberBand = None
        self.vertexMarker = None
        self.capturedPoints = []
        self.derivedPoint = None
        self.capturing = False
        self.setCursor(Qt.CrossCursor)

    def canvasReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if not self.capturing:
                self.startCapturing()
            self.addVertex(event.pos())
        elif event.button() == Qt.RightButton:
            point = self.getDerivedPoint()
            polygon = self.getCapturedPolygon()
            self.stopCapturing()
            if point != None and polygon != None:
                pointGeom = self.getPointGeometry(point)
                polygonGeom = self.getPolygonGeometry(polygon)
                if pointGeom != None and polygonGeom != None:
                    self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs())
                else:
                    self.clearScene()
            else:
                self.clearScene()

    def canvasMoveEvent(self, event):
        if self.tempRubberBand != None and self.capturing:
            mapPt = self.transformCoordinates(event.pos())
            self.tempRubberBand.movePoint(mapPt)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete:
            self.removeLastVertex()
            event.ignore()
        if event.key() == Qt.Key_Escape:
            self.stopCapturing()
            self.clearScene()
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            point = self.getDerivedPoint()
            polygon = self.getCapturedPolygon()
            self.stopCapturing()
            if point != None and polygon != None:
                pointGeom = self.getPointGeometry(point)
                polygonGeom = self.getPolygonGeometry(polygon)
                if pointGeom != None and polygonGeom != None:
                    self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs())
                else:
                    self.clearScene()
            else:
                self.clearScene()

    def startCapturing(self):
        self.clearScene()

        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.rubberBand.setWidth(2)
        self.rubberBand.setFillColor(QColor(220, 0, 0, 120))
        self.rubberBand.setBorderColor(QColor(220, 0, 0))
        self.rubberBand.setLineStyle(Qt.DotLine)
        self.rubberBand.show()

        self.tempRubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.tempRubberBand.setWidth(2)
        self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0))
        self.tempRubberBand.setBorderColor(QColor(220, 0, 0))
        self.tempRubberBand.setLineStyle(Qt.DotLine)
        self.tempRubberBand.show()

        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setIconType(1)
        self.vertexMarker.setColor(QColor(220, 0, 0))
        self.vertexMarker.setIconSize(16)
        self.vertexMarker.setPenWidth(3)
        self.vertexMarker.show()

        self.capturing = True

    def clearScene(self):
        if self.vertexMarker:
            self.canvas.scene().removeItem(self.vertexMarker)
            self.vertexMarker = None
        if self.rubberBand:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None

    def stopCapturing(self):
        if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices() < 3:
            self.canvas.scene().removeItem(self.vertexMarker)
            self.vertexMarker = None
        if self.rubberBand and self.rubberBand.numberOfVertices() < 3:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None
        self.capturing = False
        self.capturedPoints = []
        self.derivedPoint = None
        self.canvas.refresh()

    def addVertex(self, canvasPoint):
        mapPt = self.transformCoordinates(canvasPoint)

        self.rubberBand.addPoint(mapPt)
        self.capturedPoints.append(mapPt)

        bandSize = self.rubberBand.numberOfVertices()
        if bandSize > 2:

            rubGeom = self.rubberBand.asGeometry()
            cpGeom = rubGeom.centroid()
            if not rubGeom.contains(cpGeom):
                cpGeom = rubGeom.pointOnSurface()
            #nearestCp = rubGeom.nearestPoint(cpGeom)
            self.vertexMarker.setCenter(cpGeom.asPoint())
            self.derivedPoint = cpGeom.asPoint()
            self.vertexMarker.show()



        self.tempRubberBand.reset(QGis.Polygon)
        firstPoint = self.rubberBand.getPoint(0, 0)
        self.tempRubberBand.addPoint(firstPoint)
        self.tempRubberBand.movePoint(mapPt)
        self.tempRubberBand.addPoint(mapPt)

    def removeLastVertex(self):
        if not self.capturing:
            return

        bandSize = self.rubberBand.numberOfVertices()
        tempBandSize = self.tempRubberBand.numberOfVertices()
        numPoints = len(self.capturedPoints)

        if bandSize < 1 or numPoints < 1:
            return

        self.rubberBand.removePoint(-1)

        if bandSize > 1:
            if tempBandSize > 1:
                point = self.rubberBand.getPoint(0, bandSize - 2)
                self.tempRubberBand.movePoint(tempBandSize - 2, point)
        else:
            self.tempRubberBand.reset(QGis.Polygon)

        bandSize = self.rubberBand.numberOfVertices()
        if bandSize < 3:
            self.vertexMarker.hide()
        else:
            rubGeom = self.rubberBand.asGeometry()
            cpGeom = rubGeom.centroid()
            if not rubGeom.contains(cpGeom):
                cpGeom = rubGeom.pointOnSurface()
            #nearestCp = rubGeom.nearestPoint(cpGeom)
            self.vertexMarker.setCenter(cpGeom.asPoint())
            self.derivedPoint = cpGeom.asPoint()
            self.vertexMarker.show()


        del self.capturedPoints[-1]

    def getCapturedPolygon(self):
        polygon = self.capturedPoints

        if len(polygon) < 3:
            return None
        else:
            return polygon

    def getDerivedPoint(self):
        point = self.derivedPoint

        if point == None:
            return None
        else:
            return point

    def getPointGeometry(self, geom):
        p = QgsGeometry.fromPoint(geom)
        if p.isGeosValid() and not p.isGeosEmpty():
            return p
        else:
            return None

    def getPolygonGeometry(self, geom):
        p = QgsGeometry.fromPolygon([geom])
        if p.isGeosValid() and not p.isGeosEmpty():
            return p
        else:
            return None