Exemplo n.º 1
0
class QGISRedSelectPointTool(QgsMapTool):
    def __init__(self, button, parent, method, type=1):
        QgsMapTool.__init__(self, parent.iface.mapCanvas())
        self.canvas = parent.iface.mapCanvas()
        self.iface = parent.iface
        self.parent = parent
        self.method = method
        self.setAction(button)
        self.type = type

        # type 1: points; 2: lines; 3: 2-points; 4: 2-line; 5: point-line

        self.startMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.startMarker.setColor(QColor(255, 87, 51))
        if self.type == 3 or self.type == 4 or self.type == 5:
            self.startMarker.setColor(QColor(139, 0, 0))
        self.startMarker.setIconSize(15)
        self.startMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        if self.type == 2 or self.type == 4:
            self.startMarker.setIconType(
                QgsVertexMarker.ICON_X)  # or ICON_CROSS, ICON_X
        self.startMarker.setPenWidth(3)
        self.startMarker.hide()

        self.endMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.endMarker.setColor(QColor(0, 128, 0))
        self.endMarker.setIconSize(15)
        self.endMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        if self.type == 4 or self.type == 5:
            self.endMarker.setIconType(
                QgsVertexMarker.ICON_X)  # or ICON_CROSS, ICON_X
        self.endMarker.setPenWidth(3)
        self.endMarker.hide()
        self.firstPoint = None

        self.snapper = None
        self.resetProperties()

    def activate(self):
        QgsMapTool.activate(self)
        type = 1
        if self.type == 2 or self.type == 4:
            type = 2
        self.configSnapper(type)

    def deactivate(self):
        self.resetProperties()
        QgsMapTool.deactivate(self)

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True

    """Methods"""

    def configSnapper(self, type):
        # Snapping
        self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas())
        self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings())
        config = QgsSnappingConfig(QgsProject.instance())
        config.setType(type)  # 1: Vertex; 2:Segment
        config.setMode(2)  # All layers
        config.setTolerance(10)
        config.setUnits(1)  # Pixels
        config.setEnabled(True)
        self.snapper.setConfig(config)

    def resetProperties(self):
        self.firstPoint = None
        self.startMarker.hide()
        self.endMarker.hide()
        self.objectSnapped = None

    """Events"""

    def canvasReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.objectSnapped is None:
                self.iface.messageBar().pushMessage(
                    "Warning",
                    "A not valid point was selected",
                    level=1,
                    duration=5)
                return
            if self.type == 3 or self.type == 4 or self.type == 5:
                if self.firstPoint is None:
                    self.firstPoint = self.objectSnapped.point()
                    if self.type == 5:
                        self.configSnapper(2)
                else:
                    point1 = self.firstPoint
                    point2 = self.objectSnapped.point()
                    # Call to parent method
                    self.method(point1, point2)
                    self.resetProperties()
                    if self.type == 5:
                        self.configSnapper(1)
            else:
                point = self.objectSnapped.point()
                # Call to parent method
                self.method(point)
                self.resetProperties()
        if event.button() == Qt.RightButton:
            if self.type == 3 or self.type == 5:
                if self.objectSnapped is None:
                    self.iface.messageBar().pushMessage(
                        "Warning",
                        "A not valid point was selected",
                        level=1,
                        duration=5)
                    return
                else:
                    point = self.objectSnapped.point()
                    # Call to parent method
                    # Tconnection & Split/merge Juncitons
                    self.method(point, None)
                    self.resetProperties()
            else:
                self.canvas.unsetMapTool(self)
                self.deactivate()
                return

    def canvasMoveEvent(self, event):
        match = self.snapper.snapToMap(self.toMapCoordinates(event.pos()))
        if match.isValid():
            self.objectSnapped = match
            if self.firstPoint is None:
                self.startMarker.setCenter(
                    QgsPointXY(match.point().x(),
                               match.point().y()))
                self.startMarker.show()
            else:
                self.endMarker.setCenter(
                    QgsPointXY(match.point().x(),
                               match.point().y()))
                self.endMarker.show()
        else:
            self.startMarker.hide()
            self.endMarker.hide()
            self.objectSnapped = None
Exemplo n.º 2
0
class QGISRedCreatePipeTool(QgsMapTool):
    def __init__(self, button, iface, projectDirectory, netwName, parent):
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.iface = iface
        self.ProjectDirectory = projectDirectory
        self.NetworkName = netwName
        self.parent = parent
        self.setAction(button)

        self.startMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.startMarker.setColor(QColor(255, 87, 51))
        self.startMarker.setIconSize(15)
        self.startMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.startMarker.setPenWidth(3)
        self.startMarker.hide()

        self.endMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.endMarker.setColor(QColor(255, 87, 51))
        self.endMarker.setIconSize(15)
        self.endMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.endMarker.setPenWidth(3)
        self.endMarker.hide()

        self.snapper = None
        self.rubberBand1 = None
        self.rubberBand2 = None
        self.resetProperties()

    def activate(self):
        QgsMapTool.activate(self)

        # Snapping
        self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas())
        self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings())
        config = QgsSnappingConfig(QgsProject.instance())
        config.setType(1)  # Vertex
        config.setMode(2)  # All layers
        config.setTolerance(2)
        config.setUnits(2)  # Pixels
        config.setEnabled(True)
        self.snapper.setConfig(config)

    def deactivate(self):
        self.resetProperties()
        QgsMapTool.deactivate(self)

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True

    """Methods"""

    def resetProperties(self):
        # self.toolbarButton.setChecked(False)
        if self.rubberBand1 is not None:
            self.iface.mapCanvas().scene().removeItem(self.rubberBand1)
        if self.rubberBand2 is not None:
            self.iface.mapCanvas().scene().removeItem(self.rubberBand2)
        self.startMarker.hide()
        self.endMarker.hide()

        self.mousePoints = []
        self.firstClicked = False
        self.objectSnapped = None

        self.rubberBand1 = None
        self.rubberBand2 = None

    def createRubberBand(self, points):
        myPoints1 = []
        for p in points:
            myPoints1.append(QgsPoint(p.x(), p.y()))
        myPoints1.remove(myPoints1[-1])
        if self.rubberBand1 is not None:
            self.iface.mapCanvas().scene().removeItem(self.rubberBand1)
        self.rubberBand1 = QgsRubberBand(self.iface.mapCanvas(), False)
        self.rubberBand1.setToGeometry(QgsGeometry.fromPolyline(myPoints1),
                                       None)
        self.rubberBand1.setColor(QColor(240, 40, 40))
        self.rubberBand1.setWidth(1)
        self.rubberBand1.setLineStyle(Qt.SolidLine)

        myPoints2 = []
        myPoints2.append(QgsPoint(points[-2].x(), points[-2].y()))
        myPoints2.append(QgsPoint(points[-1].x(), points[-1].y()))
        if self.rubberBand2 is not None:
            self.iface.mapCanvas().scene().removeItem(self.rubberBand2)
        self.rubberBand2 = QgsRubberBand(self.iface.mapCanvas(), False)
        self.rubberBand2.setToGeometry(QgsGeometry.fromPolyline(myPoints2),
                                       None)
        self.rubberBand2.setColor(QColor(240, 40, 40))
        self.rubberBand2.setWidth(1)
        self.rubberBand2.setLineStyle(Qt.DashLine)

    """Events"""

    def canvasPressEvent(self, event):
        if event.button() == Qt.LeftButton:
            if not self.firstClicked:
                self.firstClicked = True
                point = self.toMapCoordinates(event.pos())
                if self.objectSnapped is not None:
                    point = self.objectSnapped.point()
                self.mousePoints.append(point)
                self.mousePoints.append(point)
            else:
                self.mousePoints.append(self.mousePoints[-1])
            self.createRubberBand(self.mousePoints)

        if event.button() == Qt.RightButton:
            self.mousePoints.remove(self.mousePoints[-1])
            if self.firstClicked:
                if (len(self.mousePoints) == 2
                        and self.mousePoints[0] == self.mousePoints[1]):
                    createdPipe = False
                elif len(self.mousePoints) < 2:
                    createdPipe = False
                else:
                    createdPipe = True
            if createdPipe:
                self.parent.runCreatePipe(self.mousePoints)
            self.resetProperties()

    def canvasMoveEvent(self, event):
        # Mouse not clicked
        if not self.firstClicked:
            match = self.snapper.snapToMap(self.toMapCoordinates(event.pos()))
            if match.isValid():
                self.objectSnapped = match
                self.startMarker.setCenter(
                    QgsPointXY(match.point().x(),
                               match.point().y()))
                self.startMarker.show()
            else:
                self.objectSnapped = None
                self.startMarker.hide()
        # Mouse clicked
        else:
            point = self.toMapCoordinates(event.pos())
            match = self.snapper.snapToMap(point)
            if match.isValid():
                self.objectSnapped = match
                self.endMarker.setCenter(
                    QgsPointXY(match.point().x(),
                               match.point().y()))
                self.endMarker.show()
                self.mousePoints[-1] = match.point()
            else:
                self.objectSnapped = None
                self.endMarker.hide()
                self.mousePoints[-1] = point
            self.createRubberBand(self.mousePoints)
Exemplo n.º 3
0
class QGISRedEditLinksGeometryTool(QgsMapTool):
    ownMainLayers = ["Pipes", "Valves", "Pumps", "ServiceConnections"]

    def __init__(self, button, iface, projectDirectory, netwName):
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.iface = iface
        self.ProjectDirectory = projectDirectory
        self.NetworkName = netwName
        self.toolbarButton = button

        self.snapper = None
        self.vertexMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.vertexMarker.setColor(QColor(255, 87, 51))
        self.vertexMarker.setIconSize(15)
        self.vertexMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.vertexMarker.setPenWidth(3)
        self.vertexMarker.hide()

        self.pipeSnapper = None
        self.pipeMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.pipeMarker.setColor(QColor(143, 0, 255))
        self.pipeMarker.setIconSize(10)
        try:
            self.pipeMarker.setIconType(
                QgsVertexMarker.ICON_DOUBLE_TRIANGLE)  # or ICON_CROSS, ICON_X
        except:
            self.pipeMarker.setIconType(
                QgsVertexMarker.ICON_X)  # or ICON_CROSS, ICON_X
        self.pipeMarker.setPenWidth(3)
        self.pipeMarker.hide()

        self.mouseClicked = False
        self.clickedPoint = None
        self.objectSnapped = None
        self.pipeSnapped = None
        self.selectedFeature = None
        self.selectedLayer = None
        self.newPositionVector = QgsVector(0, 0)
        self.rubberBand = None
        self.newVertexMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.newVertexMarker.setColor(QColor(55, 198, 5))
        self.newVertexMarker.setIconSize(15)
        self.newVertexMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.newVertexMarker.setPenWidth(3)
        self.newVertexMarker.hide()

    def activate(self):
        cursor = QCursor()
        cursor.setShape(Qt.ArrowCursor)
        self.iface.mapCanvas().setCursor(cursor)

        myLayers = []
        # Editing
        layers = self.getLayers()
        for layer in layers:
            openedLayerPath = self.getLayerPath(layer)
            for name in self.ownMainLayers:
                layerPath = self.generatePath(
                    self.ProjectDirectory,
                    self.NetworkName + "_" + name + ".shp")
                if openedLayerPath == layerPath:
                    myLayers.append(layer)
                    if not layer.isEditable():
                        layer.startEditing()
        # Snapping
        self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas())
        self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings())
        config = QgsSnappingConfig(QgsProject.instance())
        config.setType(2)  # Vertex
        config.setMode(2)  # All layers
        config.setTolerance(10)
        config.setUnits(1)  # Pixels
        config.setEnabled(True)
        self.snapper.setConfig(config)

        self.pipeSnapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas())
        self.pipeSnapper.setMapSettings(self.iface.mapCanvas().mapSettings())
        config = QgsSnappingConfig(QgsProject.instance())
        config.setType(2)  # Vertex
        config.setMode(2)  # All layers
        config.setTolerance(10)
        config.setUnits(1)  # Pixels
        config.setEnabled(True)
        self.pipeSnapper.setConfig(config)

    def deactivate(self):
        self.toolbarButton.setChecked(False)
        # End Editing
        layers = self.getLayers()
        for layer in layers:
            openedLayerPath = self.getLayerPath(layer)
            for name in self.ownMainLayers:
                layerPath = self.generatePath(
                    self.ProjectDirectory,
                    self.NetworkName + "_" + name + ".shp")
                if openedLayerPath == layerPath:
                    if layer.isModified():
                        layer.commitChanges()
                    else:
                        layer.rollBack()

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True

    """Methods"""

    def getUniformedPath(self, path):
        return QGISRedUtils().getUniformedPath(path)

    def getLayerPath(self, layer):
        return QGISRedUtils().getLayerPath(layer)

    def generatePath(self, folder, fileName):
        return QGISRedUtils().generatePath(folder, fileName)

    def getLayers(self):
        return QGISRedUtils().getLayers()

    def areOverlapedPoints(self, point1, point2):
        tolerance = 0.1
        if point1.distance(point2) < tolerance:
            return True
        else:
            return False

    def isInPath(self, point1, point2, myPoint):
        width = point2.x() - point1.x()
        height = point2.y() - point1.y()
        widthM = myPoint.x() - point1.x()
        heightM = myPoint.y() - point1.y()
        if abs(width) >= abs(height):
            yEst = widthM * height / width + point1.y()
            if abs(yEst - myPoint.y()) < 1E-9:
                return True
        else:
            xEst = heightM * width / height + point1.x()
            if abs(xEst - myPoint.x()) < 1E-9:
                return True
        return False

    def createRubberBand(self, points):
        myPoints = points
        if isinstance(points[0], QgsPointXY):
            myPoints = []
            for p in points:
                myPoints.append(QgsPoint(p.x(), p.y()))
        self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), False)
        self.rubberBand.setToGeometry(QgsGeometry.fromPolyline(myPoints), None)
        self.rubberBand.setColor(QColor(55, 198, 5))
        self.rubberBand.setWidth(1)
        self.rubberBand.setLineStyle(Qt.DashLine)
        self.newVertexMarker.setCenter(QgsPointXY(points[0].x(),
                                                  points[0].y()))
        self.newVertexMarker.show()

    def updateRubberBand(self):
        newX = self.clickedPoint.x() + self.newPositionVector.x()
        newY = self.clickedPoint.y() + self.newPositionVector.y()
        self.rubberBand.movePoint(1, QgsPointXY(newX, newY))
        self.newVertexMarker.setCenter(QgsPointXY(newX, newY))

    def moveVertexLink(self, layer, feature, newPosition, vertexIndex):
        if layer.isEditable():
            layer.beginEditCommand("Update link geometry")
            try:
                edit_utils = QgsVectorLayerEditUtils(layer)
                edit_utils.moveVertex(newPosition.x(), newPosition.y(),
                                      feature.id(), vertexIndex)
            except Exception as e:
                layer.destroyEditCommand()
                raise e
            layer.endEditCommand()

    def deleteVertexLink(self, layer, feature, vertexIndex):
        if layer.isEditable():
            layer.beginEditCommand("Update link geometry")
            try:
                edit_utils = QgsVectorLayerEditUtils(layer)
                edit_utils.deleteVertex(feature.id(), vertexIndex)
            except Exception as e:
                layer.destroyEditCommand()
                raise e
            layer.endEditCommand()

    def insertVertexLink(self, layer, feature, newPoint):
        if layer.isEditable():
            layer.beginEditCommand("Update link geometry")
            vertex = -1
            if layer.geometryType() == 1:  # Line
                featureGeometry = self.selectedFeature.geometry()
                if featureGeometry.isMultipart():
                    parts = featureGeometry.get()
                    for part in parts:  # only one part
                        for i in range(len(part) - 1):
                            if self.isInPath(
                                    QgsPointXY(part[i].x(), part[i].y()),
                                    QgsPointXY(part[i + 1].x(),
                                               part[i + 1].y()), newPoint):
                                vertex = i + 1
            try:
                edit_utils = QgsVectorLayerEditUtils(layer)
                edit_utils.insertVertex(newPoint.x(), newPoint.y(),
                                        feature.id(), vertex)
            except Exception as e:
                layer.destroyEditCommand()
                raise e
            layer.endEditCommand()

    """Events"""

    def canvasPressEvent(self, event):
        if self.objectSnapped is None:
            self.clickedPoint = None
            return

        if event.button() == Qt.RightButton:
            self.mouseClicked = False
            self.clickedPoint = None

        if event.button() == Qt.LeftButton:
            self.clickedPoint = self.objectSnapped.point()
            if self.vertexIndex == -1:
                return
            self.mouseClicked = True
            self.createRubberBand(
                [self.objectSnapped.point(),
                 self.objectSnapped.point()])

    def canvasMoveEvent(self, event):
        mousePoint = self.toMapCoordinates(event.pos())
        # Mouse not clicked
        if not self.mouseClicked:
            self.pipeSnappedOn = False
            matchSnapper = self.snapper.snapToMap(mousePoint)
            if matchSnapper.isValid():
                valid = False
                layer = matchSnapper.layer()
                snapLayerPath = self.getLayerPath(layer)
                for name in self.ownMainLayers:
                    layerPath = self.generatePath(
                        self.ProjectDirectory,
                        self.NetworkName + "_" + name + ".shp")
                    if snapLayerPath == layerPath:
                        valid = True
                if valid:
                    self.objectSnapped = matchSnapper
                    self.selectedLayer = layer

                    vertex = matchSnapper.point()
                    featureId = matchSnapper.featureId()
                    request = QgsFeatureRequest().setFilterFid(featureId)
                    nodes = list(layer.getFeatures(request))
                    self.selectedFeature = QgsFeature(nodes[0])
                    # #Ver aquí si es el nudo inicial y final
                    middleNode = False
                    self.vertexIndex = -1
                    if layer.geometryType() == 1:  # Line
                        featureGeometry = self.selectedFeature.geometry()
                        if featureGeometry.isMultipart():
                            parts = featureGeometry.get()
                            for part in parts:  # only one part
                                if middleNode:
                                    break
                                i = -1
                                for v in part:
                                    i = i + 1
                                    if (
                                            i == 0 or i == len(part) - 1
                                    ) and "ServiceConnections" not in snapLayerPath:
                                        continue

                                    matchedPoint = QgsPointXY(
                                        vertex.x(), vertex.y())
                                    if self.areOverlapedPoints(
                                            QgsGeometry.fromPointXY(
                                                matchedPoint),
                                            QgsGeometry.fromPointXY(
                                                QgsPointXY(v.x(), v.y()))):
                                        middleNode = True
                                        self.vertexIndex = i
                                        if (
                                                i == 0 or i == len(part) - 1
                                        ) and "ServiceConnections" in snapLayerPath:
                                            self.pipeSnappedOn = True
                                        break
                    if middleNode:
                        self.vertexMarker.setCenter(
                            QgsPointXY(vertex.x(), vertex.y()))
                        self.vertexMarker.show()
                    else:
                        self.vertexMarker.hide()
                else:
                    self.objectSnapped = None
                    self.selectedFeature = None
                    self.selectedLayer = None
                    self.vertexMarker.hide()
            else:
                self.objectSnapped = None
                self.selectedFeature = None
                self.selectedLayer = None
                self.vertexMarker.hide()
        # Mouse clicked
        else:
            # Snap pipe layer
            if self.pipeSnappedOn:
                matchSnapper = self.pipeSnapper.snapToMap(mousePoint)
                if matchSnapper.isValid():
                    valid = False
                    layer = matchSnapper.layer()
                    snapLayerPath = self.getLayerPath(layer)
                    for name in self.ownMainLayers:
                        layerPath = self.generatePath(
                            self.ProjectDirectory,
                            self.NetworkName + "_Pipes.shp")
                        if snapLayerPath == layerPath:
                            valid = True
                    if valid:
                        self.pipeSnapped = matchSnapper
                        self.pipeMarker.setCenter(matchSnapper.point())
                        self.pipeMarker.show()
                    else:
                        self.pipeMarker.hide()
                else:
                    self.pipeMarker.hide()
                    self.pipeSnapped = None

            # # Update rubber band
            if self.objectSnapped is not None and self.rubberBand is not None:
                snappedPoint = self.objectSnapped.point()
                self.newPositionVector = QgsVector(
                    mousePoint.x() - snappedPoint.x(),
                    mousePoint.y() - snappedPoint.y())
                self.updateRubberBand()

    def canvasReleaseEvent(self, event):
        if self.mouseClicked:
            if event.button() == 1:
                mousePoint = self.toMapCoordinates(event.pos())
                if (self.pipeSnapped is not None):
                    mousePoint = self.pipeSnapped.point()
                self.mouseClicked = False
                if self.objectSnapped is not None:
                    self.moveVertexLink(self.selectedLayer,
                                        self.selectedFeature, mousePoint,
                                        self.vertexIndex)
        elif event.button() == 2:
            if self.objectSnapped is not None:
                self.deleteVertexLink(self.selectedLayer, self.selectedFeature,
                                      self.vertexIndex)
        elif event.button() == 1:
            if self.objectSnapped is not None:
                self.insertVertexLink(self.selectedLayer, self.selectedFeature,
                                      self.objectSnapped.point())
        self.objectSnapped = None
        self.pipeSnapped = None
        self.selectedFeature = None
        self.selectedLayer = None
        self.vertexIndex = -1
        self.iface.mapCanvas().refresh()
        # Remove vertex marker and rubber band
        self.vertexMarker.hide()
        self.iface.mapCanvas().scene().removeItem(self.rubberBand)
        self.newVertexMarker.hide()
        self.pipeMarker.hide()
Exemplo n.º 4
0
class QgepMapToolAddReach(QgepMapToolAddFeature):
    """
    Create a new reach with the mouse.
    Will snap to wastewater nodes for the first and last point and auto-connect
    these.
    """
    first_snapping_match = None
    last_snapping_match = None
    last_feature_attributes = None

    def __init__(self, iface: QgisInterface, layer):
        QgepMapToolAddFeature.__init__(self, iface, layer)
        self.snapping_marker = None
        self.node_layer = QgepLayerManager.layer('vw_wastewater_node')
        assert self.node_layer is not None
        self.reach_layer = QgepLayerManager.layer('vw_qgep_reach')
        assert self.reach_layer is not None
        self.setAdvancedDigitizingAllowed(True)
        self.setAutoSnapEnabled(True)

        layer_snapping_configs = [{
            'layer': self.node_layer,
            'mode': QgsSnappingConfig.Vertex
        }, {
            'layer': self.reach_layer,
            'mode': QgsSnappingConfig.VertexAndSegment
        }]
        self.snapping_configs = []
        self.snapping_utils = QgsMapCanvasSnappingUtils(self.iface.mapCanvas())

        for lsc in layer_snapping_configs:
            config = QgsSnappingConfig()
            config.setMode(QgsSnappingConfig.AdvancedConfiguration)
            config.setEnabled(True)
            settings = QgsSnappingConfig.IndividualLayerSettings(
                True, lsc['mode'], 10, QgsTolerance.Pixels)
            config.setIndividualLayerSettings(lsc['layer'], settings)
            self.snapping_configs.append(config)

    def left_clicked(self, event):
        """
        The mouse is clicked: snap to neary points which are on the wastewater node layer
        and update the rubberband
        :param event: The coordinates etc.
        """
        point3d, match = self.snap(event)
        if self.rubberband.numberOfVertices() == 0:
            self.first_snapping_match = match
        self.last_snapping_match = match
        self.rubberband.addPoint3D(point3d)
        self.temp_rubberband.reset()
        self.temp_rubberband.addPoint(QgsPointXY(point3d.x(), point3d.y()))

        if self.snapping_marker is not None:
            self.iface.mapCanvas().scene().removeItem(self.snapping_marker)
            self.snapping_marker = None

    def mouse_move(self, event):
        _, match = self.snap(event)
        # snap indicator
        if not match.isValid():
            if self.snapping_marker is not None:
                self.iface.mapCanvas().scene().removeItem(self.snapping_marker)
                self.snapping_marker = None
            return

        # TODO QGIS 3: see if vertices can be removed

        # we have a valid match
        if self.snapping_marker is None:
            self.snapping_marker = QgsVertexMarker(self.iface.mapCanvas())
            self.snapping_marker.setPenWidth(3)
            self.snapping_marker.setColor(QColor(Qt.magenta))

        if match.hasVertex():
            if match.layer():
                icon_type = QgsVertexMarker.ICON_BOX  # vertex snap
            else:
                icon_type = QgsVertexMarker.ICON_X  # intersection snap
        else:
            icon_type = QgsVertexMarker.ICON_DOUBLE_TRIANGLE  # must be segment snap
        self.snapping_marker.setIconType(icon_type)
        self.snapping_marker.setCenter(match.point())

    def snap(self, event):
        """
        Snap to nearby points on the wastewater node layer which may be used as connection
        points for this reach.
        :param event: The mouse event
        :return: The snapped position in map coordinates
        """

        for config in self.snapping_configs:
            self.snapping_utils.setConfig(config)
            match = self.snapping_utils.snapToMap(
                QgsPointXY(event.originalMapPoint()))
            if match.isValid():
                return QgsPoint(match.point()), match

        # if no match, snap to all layers (according to map settings) and try to grab Z
        match = self.iface.mapCanvas().snappingUtils().snapToMap(
            QgsPointXY(event.originalMapPoint()))
        if match.isValid() and match.hasVertex():
            if match.layer():
                req = QgsFeatureRequest(match.featureId())
                f = next(match.layer().getFeatures(req))
                assert f.isValid()
                (ok, vertex_id) = f.geometry().vertexIdFromVertexNr(
                    match.vertexIndex())
                assert ok
                point = f.geometry().constGet().vertexAt(vertex_id)
                assert type(point) == QgsPoint
                return point, match
            else:
                return QgsPoint(match.point()), match

        return QgsPoint(event.originalMapPoint()), match

    def right_clicked(self, _):
        """
        The party is over, the reach digitized. Create a feature from the rubberband and
        show the feature form.
        """
        self.temp_rubberband.reset()

        if self.snapping_marker is not None:
            self.iface.mapCanvas().scene().removeItem(self.snapping_marker)
            self.snapping_marker = None

        if len(self.rubberband.points) >= 2:

            fields = self.layer.fields()
            f = QgsFeature(fields)
            if not self.last_feature_attributes:
                self.last_feature_attributes = [None] * fields.count()
            for idx, field in enumerate(fields):
                if field.name() in [
                        'clear_height', 'material', 'ch_usage_current',
                        'ch_function_hierarchic', 'ch_function_hydraulic',
                        'horizontal_positioning', 'ws_status',
                        'ws_year_of_construction', 'ws_fk_owner',
                        'ws_fk_operator', 'inside_coating', 'fk_pipe_profile',
                        'remark'
                ]:
                    f.setAttribute(idx, self.last_feature_attributes[idx])
                else:
                    # try client side default value first
                    v = self.layer.defaultValue(idx, f)
                    if v != NULL:
                        f.setAttribute(idx, v)
                    else:
                        f.setAttribute(
                            idx,
                            self.layer.dataProvider().defaultValue(idx))

            f.setGeometry(self.rubberband.asGeometry3D())

            snapping_results = {
                'from': self.first_snapping_match,
                'to': self.last_snapping_match
            }
            for dest, match in list(snapping_results.items()):
                level_field_index = self.layer.fields().indexFromName(
                    'rp_{dest}_level'.format(dest=dest))
                pt_idx = 0 if dest == 'from' else -1
                if match.isValid() and match.layer() in (self.node_layer,
                                                         self.reach_layer):
                    request = QgsFeatureRequest(match.featureId())
                    network_element = next(match.layer().getFeatures(request))
                    assert network_element.isValid()
                    # set the related network element
                    field = self.layer.fields().indexFromName(
                        'rp_{dest}_fk_wastewater_networkelement'.format(
                            dest=dest))
                    f.setAttribute(field, network_element.attribute('obj_id'))
                    # assign level if the match is a node or if we have 3D from snapping
                    if match.layer() == self.node_layer:
                        level = network_element['bottom_level']
                        f.setAttribute(level_field_index, level)
                elif self.rubberband.points[pt_idx].z() != 0:
                    level = self.rubberband.points[pt_idx].z()
                    level = level if not math.isnan(level) else NULL
                    f.setAttribute(level_field_index, level)

            dlg = self.iface.getFeatureForm(self.layer, f)
            dlg.setMode(QgsAttributeEditorContext.AddFeatureMode)
            dlg.exec_()
            self.last_feature_attributes = dlg.feature().attributes()

        self.rubberband.reset3D()
Exemplo n.º 5
0
class QgepMapTool(QgsMapTool):
    """
    Base class for all the map tools
    """

    highlightedPoints = []
    logger = logging.getLogger(__name__)
    snapper = None

    def __init__(self, iface: QgisInterface, button, network_analyzer: QgepGraphManager = None):
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.canvas = iface.mapCanvas()
        self.cursor = QCursor(Qt.CrossCursor)
        self.button = button
        self.msgBar = iface.messageBar()
        self.network_analyzer = network_analyzer

        settings = QSettings()
        current_profile_color = settings.value(
            "/QGEP/CurrentProfileColor", '#FF9500')

        self.rubberBand = QgsRubberBand(self.canvas)
        self.rubberBand.setColor(QColor(current_profile_color))
        self.rubberBand.setWidth(3)

    def activate(self):
        """
        Gets called when the tool is activated
        """
        QgsMapTool.activate(self)
        self.canvas.setCursor(self.cursor)
        self.button.setChecked(True)

    def deactivate(self):
        """
        Gets called whenever the tool is deactivated directly or indirectly
        """
        QgsMapTool.deactivate(self)
        self.button.setChecked(False)

    # pylint: disable=no-self-use
    def isZoomTool(self):
        """
        Will return if this is a zoom tool
        """
        return False

    def setCursor(self, cursor):
        """
        Set the cursor for this maptool
        """
        self.cursor = QCursor(cursor)

    # ===========================================================================
    # Events
    # ===========================================================================

    def canvasReleaseEvent(self, event):
        """
        Issues rightClicked and leftClicked events
        """
        if event.button() == Qt.RightButton:
            self.rightClicked(event)
        else:
            self.leftClicked(event)

    def canvasDoubleClickEvent(self, event):
        """
        Forwards to doubleClicked
        """
        try:
            self.doubleClicked(event)
        except AttributeError:
            pass

    # ===========================================================================
    # Snapping
    # ===========================================================================
    def init_snapper(self):
        """
        Initialize snapper
        """
        if not self.snapper:
            self.node_layer = self.network_analyzer.getNodeLayer()
            self.snapper = QgsMapCanvasSnappingUtils(self.canvas)
            config = QgsSnappingConfig()
            config.setMode(QgsSnappingConfig.AdvancedConfiguration)
            config.setEnabled(True)
            ils = QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.VertexAndSegment,
                                                            16, QgsTolerance.Pixels)
            config.setIndividualLayerSettings(self.node_layer, ils)
            self.snapper.setConfig(config)

    def snap_point(self, event, show_menu: bool = True) -> QgsPointLocator.Match:
        """
        Snap to a point on this network
        :param event: A QMouseEvent
        :param show_menu: determines if a menu shall be shown on a map if several matches are available
        """
        clicked_point = event.pos()

        if not self.snapper:
            self.init_snapper()

        match_filter = CounterMatchFilter()
        match = self.snapper.snapToMap(clicked_point, match_filter)

        if not match.isValid() or len(match_filter.matches) == 1:
            return match
        elif len(match_filter.matches) > 1:
            matches_by_id = {match.featureId(): match for match in match_filter.matches}
            node_features = self.network_analyzer.getFeaturesById(self.network_analyzer.getNodeLayer(),
                                                                  list(matches_by_id.keys()))

            # Filter wastewater nodes
            filtered_features = {
                fid: node_features.featureById(fid)
                for fid in node_features.asDict()
                if node_features.attrAsUnicode(node_features.featureById(fid), 'type') == 'wastewater_node'
            }

            # Only one wastewater node left: return this
            if len(filtered_features) == 1:
                matches = (match for match
                           in match_filter.matches
                           if match.featureId() == next(iter(filtered_features.keys())))
                return next(matches)

            # Still not sure which point to take?
            # Are there no wastewater nodes filtered? Let the user choose from the reach points
            if not filtered_features:
                filtered_features = node_features.asDict()

            # Ask the user which point he wants to use
            if not show_menu:
                return QgsPointLocator.Match()

            actions = dict()

            menu = QMenu(self.canvas)

            for fid, feature in filtered_features.items():
                try:
                    title = feature.attribute('description') + " (" + feature.attribute('obj_id') + ")"
                except TypeError:
                    title = " (" + feature.attribute('obj_id') + ")"
                actions[QAction(title, menu)] = matches_by_id[fid]

            for action in sorted(actions.keys(), key=lambda o: o.text()):
                menu.addAction(action)

            clicked_action = menu.exec_(self.canvas.mapToGlobal(event.pos()))

            if clicked_action is not None:
                return actions[clicked_action]

            return QgsPointLocator.Match()
Exemplo n.º 6
0
class QGISRedMoveNodesTool(QgsMapTool):
    ownMainLayers = [
        "Pipes", "Valves", "Pumps", "Junctions", "Tanks", "Reservoirs",
        "Demands", "Sources"
    ]
    myNodeLayers = ["Junctions", "Tanks", "Reservoirs", "Demands", "Sources"]

    def __init__(self, button, iface, projectDirectory, netwName):
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.iface = iface
        self.ProjectDirectory = projectDirectory
        self.NetworkName = netwName
        self.toolbarButton = button

        self.snapper = None
        self.vertexMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.vertexMarker.setColor(QColor(255, 87, 51))
        self.vertexMarker.setIconSize(15)
        self.vertexMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.vertexMarker.setPenWidth(3)
        self.vertexMarker.hide()
        self.mousePoint = None

        self.mouseClicked = False
        self.clickedPoint = None
        self.objectSnapped = None
        self.selectedNodeFeature = None
        self.selectedNodeLayer = None
        self.adjacentFeatures = None
        self.newPositionVector = QgsVector(0, 0)
        self.rubberBand = None
        self.newVertexMarker = QgsVertexMarker(self.iface.mapCanvas())
        self.newVertexMarker.setColor(QColor(55, 198, 5))
        self.newVertexMarker.setIconSize(15)
        self.newVertexMarker.setIconType(
            QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.newVertexMarker.setPenWidth(3)
        self.newVertexMarker.hide()

    def activate(self):
        cursor = QCursor()
        cursor.setShape(Qt.ArrowCursor)
        self.iface.mapCanvas().setCursor(cursor)

        myLayers = []
        # Editing
        layers = self.getLayers()
        for layer in layers:
            openedLayerPath = self.getLayerPath(layer)
            for name in self.ownMainLayers:
                layerPath = self.generatePath(
                    self.ProjectDirectory,
                    self.NetworkName + "_" + name + ".shp")
                if openedLayerPath == layerPath:
                    myLayers.append(layer)
                    if not layer.isEditable():
                        layer.startEditing()
        # Snapping
        self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas())
        self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings())
        config = QgsSnappingConfig(QgsProject.instance())
        config.setType(1)  # Vertex
        config.setMode(2)  # All layers
        config.setTolerance(1)
        config.setUnits(2)  # Pixels
        config.setEnabled(True)
        self.snapper.setConfig(config)

    def deactivate(self):
        self.toolbarButton.setChecked(False)
        # End Editing
        layers = self.getLayers()
        for layer in layers:
            openedLayerPath = self.getLayerPath(layer)
            for name in self.ownMainLayers:
                layerPath = self.generatePath(
                    self.ProjectDirectory,
                    self.NetworkName + "_" + name + ".shp")
                if openedLayerPath == layerPath:
                    if layer.isModified():
                        layer.commitChanges()
                    else:
                        layer.rollBack()

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True

    """Methods"""

    def getUniformedPath(self, path):
        return QGISRedUtils().getUniformedPath(path)

    def getLayerPath(self, layer):
        return QGISRedUtils().getLayerPath(layer)

    def generatePath(self, folder, fileName):
        return QGISRedUtils().generatePath(folder, fileName)

    def getLayers(self):
        return QGISRedUtils().getLayers()

    def findAdjacentElements(self, nodeGeometry):
        adjacentElements = {}
        layers = self.getLayers()
        for layer in layers:
            openedLayerPath = self.getLayerPath(layer)
            for name in self.ownMainLayers:
                layePath = self.generatePath(
                    self.ProjectDirectory,
                    self.NetworkName + "_" + name + ".shp")
                if openedLayerPath == layePath:
                    adjacentFeatures = []
                    for feature in layer.getFeatures():
                        featureGeometry = feature.geometry()
                        if layer.geometryType() == 0:  # Point
                            if self.areOverlapedPoints(nodeGeometry,
                                                       featureGeometry):
                                adjacentFeatures.append(feature)
                        elif layer.geometryType() == 1:
                            if featureGeometry.isMultipart():
                                for part in featureGeometry.get(
                                ):  # only one part
                                    first_vertex = part[0]
                                    last_vertex = part[-1]
                            else:
                                first_vertex = featureGeometry.get()[0]
                                last_vertex = featureGeometry.get()[-1]

                            firsVertex = QgsGeometry.fromPointXY(
                                QgsPointXY(first_vertex.x(), first_vertex.y()))
                            lastVertex = QgsGeometry.fromPointXY(
                                QgsPointXY(last_vertex.x(), last_vertex.y()))
                            if self.areOverlapedPoints(nodeGeometry, firsVertex) or\
                                    self.areOverlapedPoints(nodeGeometry, lastVertex):
                                adjacentFeatures.append(feature)
                    if len(adjacentFeatures) > 0:
                        adjacentElements[layer] = adjacentFeatures

        return adjacentElements

    def areOverlapedPoints(self, point1, point2):
        tolerance = 0.1
        if point1.distance(point2) < tolerance:
            return True
        else:
            return False

    def createRubberBand(self, points):
        myPoints = points
        if isinstance(points[0], QgsPointXY):
            myPoints = []
            for p in points:
                myPoints.append(QgsPoint(p.x(), p.y()))
        self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), False)
        self.rubberBand.setToGeometry(QgsGeometry.fromPolyline(myPoints), None)
        self.rubberBand.setColor(QColor(55, 198, 5))
        self.rubberBand.setWidth(1)
        self.rubberBand.setLineStyle(Qt.DashLine)
        self.newVertexMarker.setCenter(QgsPointXY(points[0].x(),
                                                  points[0].y()))
        self.newVertexMarker.show()

    def updateRubberBand(self):
        newX = self.clickedPoint.x() + self.newPositionVector.x()
        newY = self.clickedPoint.y() + self.newPositionVector.y()
        self.rubberBand.movePoint(1, QgsPointXY(newX, newY))
        self.newVertexMarker.setCenter(QgsPointXY(newX, newY))

    def moveNodePoint(self, layer, nodeFeature, newPosition):
        if layer.isEditable():
            layer.beginEditCommand('Move node')
            try:
                edit_utils = QgsVectorLayerEditUtils(layer)
                edit_utils.moveVertex(newPosition.x(), newPosition.y(),
                                      nodeFeature.id(), 0)
            except Exception as e:
                layer.destroyEditCommand()
                raise e
            layer.endEditCommand()

    def moveVertexLink(self, layer, feature, newPosition, vertexIndex):
        if layer.isEditable():
            layer.beginEditCommand("Update link geometry")
            try:
                edit_utils = QgsVectorLayerEditUtils(layer)
                edit_utils.moveVertex(newPosition.x(), newPosition.y(),
                                      feature.id(), vertexIndex)
            except Exception as e:
                layer.destroyEditCommand()
                raise e
            layer.endEditCommand()

    """Events"""

    def canvasPressEvent(self, event):
        if self.objectSnapped is None:
            self.clickedPoint = None
            return

        if event.button() == Qt.RightButton:
            self.mouseClicked = False
            self.clickedPoint = None

        if event.button() == Qt.LeftButton:
            self.mouseClicked = True
            self.clickedPoint = self.objectSnapped.point()

            self.selectedNodeFeature = None
            self.adjacentFeatures = None

            foundNode = False
            layers = self.getLayers()
            for layer in layers:
                openedLayerPath = self.getLayerPath(layer)
                for name in self.myNodeLayers:
                    layerPath = self.generatePath(
                        self.ProjectDirectory,
                        self.NetworkName + "_" + name + ".shp")
                    if openedLayerPath == layerPath:
                        locatedPoint = self.snapper.locatorForLayer(layer)
                        match = locatedPoint.nearestVertex(
                            self.objectSnapped.point(), 1)
                        if match.isValid():
                            featureId = match.featureId()
                            request = QgsFeatureRequest().setFilterFid(
                                featureId)
                            node = list(layer.getFeatures(request))
                            self.selectedNodeLayer = layer
                            foundNode = True

            if not foundNode:
                return

            self.selectedNodeFeature = QgsFeature(node[0])
            self.adjacentFeatures = self.findAdjacentElements(
                self.selectedNodeFeature.geometry())
            self.createRubberBand(
                [self.objectSnapped.point(),
                 self.objectSnapped.point()])

    def canvasMoveEvent(self, event):
        self.mousePoint = self.toMapCoordinates(event.pos())
        # Mouse not clicked
        if not self.mouseClicked:
            match = self.snapper.snapToMap(self.mousePoint)
            if match.isValid():
                self.objectSnapped = match
                vertex = match.point()
                self.vertexMarker.setCenter(QgsPointXY(vertex.x(), vertex.y()))
                self.vertexMarker.show()
            else:
                self.objectSnapped = None
                self.selectedNodeFeature = None
                self.vertexMarker.hide()
        # Mouse clicked
        else:
            # Update rubber band
            if self.objectSnapped is not None and self.rubberBand is not None:
                snappedPoint = self.objectSnapped.point()
                self.newPositionVector = QgsVector(
                    self.mousePoint.x() - snappedPoint.x(),
                    self.mousePoint.y() - snappedPoint.y())
                self.updateRubberBand()

    def canvasReleaseEvent(self, event):
        mousePoint = self.toMapCoordinates(event.pos())
        if not self.mouseClicked:
            return

        if event.button() == 1:
            self.mouseClicked = False

            if self.objectSnapped is not None:
                if self.selectedNodeFeature is not None:
                    for adjLayer in self.adjacentFeatures:
                        for feature in self.adjacentFeatures[adjLayer]:
                            if adjLayer.geometryType() == 0:  # Point
                                self.moveNodePoint(adjLayer, feature,
                                                   mousePoint)
                            else:
                                nodeGeometry = self.selectedNodeFeature.geometry(
                                )
                                featureGeometry = feature.geometry()

                                if featureGeometry.isMultipart():
                                    for part in featureGeometry.get(
                                    ):  # only one part
                                        firstVertex = part[0]
                                        vertices = len(part)
                                else:
                                    firstVertex = featureGeometry.get()[0]
                                    vertices = 2
                                firstPoint = QgsGeometry.fromPointXY(
                                    QgsPointXY(firstVertex.x(),
                                               firstVertex.y()))
                                if self.areOverlapedPoints(
                                        nodeGeometry, firstPoint):
                                    index = 0
                                else:
                                    index = vertices - 1
                                self.moveVertexLink(adjLayer, feature,
                                                    mousePoint, index)
                self.objectSnapped = None
                self.selectedNodeFeature = None
                self.iface.mapCanvas().refresh()

            # Remove vertex marker and rubber band
            self.vertexMarker.hide()
            self.iface.mapCanvas().scene().removeItem(self.rubberBand)
            self.newVertexMarker.hide()