Beispiel #1
0
    def getIntersection(self, p1, p2):
        """
        Get intersection on segment
        :param p1: QgsPointV2
        :param p2: QgsPointV2
        :param inter: QgsPointV2 reference
        :return: bool
        """
        q1 = self.idxFrom.point()
        q2 = self.idxTo.point()
        v = QgsVector(p2.x() - p1.x(), p2.y() - p1.y())
        w = QgsVector(q2.x() - q1.x(), q2.y() - q1.y())
        vl = v.length()
        wl = w.length()

        if self.isclose(vl, 0.) or self.isclose(wl, 0.):
            return None

        v = v / vl
        w = w / wl

        d = v.y() * w.x() - v.x() * w.y()

        if d == 0:
            return None

        dx = q1.x() - p1.x()
        dy = q1.y() - p1.y()
        k = (dy * w.x() - dx * w.y()) / d

        inter = QgsPointV2(p1.x() + v.x() * k, p1.y() + v.y() * k)

        lambdav = QgsVector(inter.x() - p1.x(), inter.y() - p1.y()) * v
        if lambdav < 0. + 1E-8 or lambdav > vl - 1E-8:
            return None

        lambdaw = QgsVector(inter.x() - q1.x(), inter.y() - q1.y()) * w
        if lambdaw < 0. + 1E-8 or lambdaw >= wl - 1E-8:
            return None

        return inter
Beispiel #2
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()
Beispiel #3
0
class MoveTool(QgsMapTool):

    def __init__(self, data_dock, params):
        QgsMapTool.__init__(self, data_dock.iface.mapCanvas())

        self.iface = data_dock.iface
        """:type : QgisInterface"""
        self.dock_widget = data_dock
        """:type : DataDock"""
        self.params = params

        self.elev = None
        self.vertex_marker = QgsVertexMarker(self.canvas())
        self.mouse_clicked = False
        self.snapper = None

        self.clicked_pt = None

        self.snap_results = None

        self.selected_node_ft = None
        self.selected_node_ft_lay = None
        self.mouse_pt = None
        self.pump_valve_selected = False
        self.pump_or_valve = None
        self.pump_valve_ft = None
        self.adj_links_fts = None
        self.adj_junctions = None
        self.delta_vec = QgsVector(0, 0)
        self.adj_links_fts_d = {}

        # self.rubber_bands_d = {}
        # self.rubber_bands_geoms_d = {}

        self.rubber_band = None

        self.logger = logging.getLogger('Logger1')

    def canvasPressEvent(self, event):

        if self.snap_results is None:
            self.clicked_pt = None
            return

        if event.button() == Qt.RightButton:
            self.mouse_clicked = False
            self.clicked_pt = None

        if event.button() == Qt.LeftButton:

            self.mouse_clicked = True
            self.clicked_pt = self.snap_results.point()

            # Check if a node was snapped: it can be just one
            self.selected_node_ft = None
            self.adj_links_fts = None

            # Check if I snapped on a node
            snapped_layer_name = self.snap_results.layer().name()

            self.selected_node_ft = None

            pt_locator_ju = self.snapper.locatorForLayer(self.params.junctions_vlay)
            pt_locator_re = self.snapper.locatorForLayer(self.params.reservoirs_vlay)
            pt_locator_ta = self.snapper.locatorForLayer(self.params.tanks_vlay)
            match_ju = pt_locator_ju.nearestVertex(self.snap_results.point(), 1)
            match_re = pt_locator_re.nearestVertex(self.snap_results.point(), 1)
            match_ta = pt_locator_ta.nearestVertex(self.snap_results.point(), 1)

            if match_ju.isValid() or match_re.isValid() or match_ta.isValid():

                if match_ju.isValid():
                    node_feat_id = match_ju.featureId()
                    request = QgsFeatureRequest().setFilterFid(node_feat_id)
                    node = list(self.params.junctions_vlay.getFeatures(request))
                    self.selected_node_ft_lay = self.params.junctions_vlay

                if match_re.isValid():
                    node_feat_id = match_re.featureId()
                    request = QgsFeatureRequest().setFilterFid(node_feat_id)
                    node = list(self.params.reservoirs_vlay.getFeatures(request))
                    self.selected_node_ft_lay = self.params.reservoirs_vlay

                if match_ta.isValid():
                    node_feat_id = match_ta.featureId()
                    request = QgsFeatureRequest().setFilterFid(node_feat_id)
                    node = list(self.params.tanks_vlay.getFeatures(request))
                    self.selected_node_ft_lay = self.params.tanks_vlay

                self.selected_node_ft = QgsFeature(node[0])

                # self.selected_node_ft_lay = self.snap_results.layer()
                self.adj_links_fts = NetworkUtils.find_adjacent_links(self.params, self.selected_node_ft.geometry())

            # No selected nodes: it's just a vertex
            if self.selected_node_ft is None:

                snapped_ft = vector_utils.get_feats_by_id(self.snap_results.layer(), self.snap_results.featureId())[0]
                snapped_ft_geom = snapped_ft.geometry()

                points = []
                if self.snap_results.vertexIndex() - 1 >= 0 and self.snap_results.vertexIndex() - 1 < len(snapped_ft_geom.asPolyline()):
                    vertex_index = 1
                    vertex_before = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex() - 1)
                    points.append(vertex_before)

                vertex_at = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex())
                points.append(vertex_at)

                if self.snap_results.vertexIndex() + 1 >= 0 and self.snap_results.vertexIndex() + 1 < len(snapped_ft_geom.asPolyline()):
                    vertex_index = 0
                    vertex_after = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex() + 1)
                    points.append(vertex_after)

                if self.snap_results.vertexIndex() > 0 and self.snap_results.vertexIndex() < len(snapped_ft_geom.asPolyline()) - 1:
                    vertex_index = 1

                # self.rubber_bands_d[0] = (self.build_rubber_band(points), [vertex_index], [vertex_at])
                self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()])

            # It's a node
            else:

                # It's an isolated node: no rubber band!
                if self.adj_links_fts is None or (not self.adj_links_fts['pipes'] and not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']):
                    # self.rubber_bands_d.clear()
                    self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()])
                    return

                # Adjacent links are neither pumps nor valves: find the two pipes adjacent to the node
                # OR node adjacent to pump or valve and NOT using block logic
                if (not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']) or not self.params.block_logic:

                    self.pump_valve_selected = False

                    rb_points = []

                    for adjacent_pipes_ft in self.adj_links_fts['pipes']:
                        closest = adjacent_pipes_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint())
                        self.adj_links_fts_d[adjacent_pipes_ft] = (closest[1], self.params.pipes_vlay)
                        if closest[1] == 0:
                            next_vertext_id = closest[1] + 1
                        else:
                            next_vertext_id = closest[1] - 1
                        rb_points.append(adjacent_pipes_ft.geometry().vertexAt(next_vertext_id))

                    for adj_pumps_ft in self.adj_links_fts['pumps']:
                        closest = adj_pumps_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint())
                        self.adj_links_fts_d[adj_pumps_ft] = (closest[1], self.params.pumps_vlay)
                        if closest[1] == 0:
                            next_vertext_id = closest[1] + 1
                        else:
                            next_vertext_id = closest[1] - 1
                        rb_points.append(adj_pumps_ft.geometry().vertexAt(next_vertext_id))

                    for adj_valves_ft in self.adj_links_fts['valves']:
                        closest = adj_valves_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint())
                        self.adj_links_fts_d[adj_valves_ft] = (closest[1], self.params.valves_vlay)
                        if closest[1] == 0:
                            next_vertext_id = closest[1] + 1
                        else:
                            next_vertext_id = closest[1] - 1
                        rb_points.append(adj_valves_ft.geometry().vertexAt(next_vertext_id))

                    rb_points.insert(1, self.selected_node_ft.geometry().asPoint())
                    # self.rubber_bands_d[0] = (self.build_rubber_band(rb_points), [1], [self.selected_node_ft.geometry().asPoint()])
                    self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()])

                # Node adjacent to pump or valve and using block logic
                else:

                    self.pump_valve_selected = True

                    # Find the pipes adjacent to the pump/valve
                    if self.adj_links_fts['pumps']:
                        self.pump_valve_ft = self.adj_links_fts['pumps'][0]
                        self.pump_or_valve = 'pump'
                        adj_links = NetworkUtils.find_links_adjacent_to_link(self.params, self.params.pumps_vlay, self.pump_valve_ft, False, True, True)
                    elif self.adj_links_fts['valves']:
                        self.pump_valve_ft = self.adj_links_fts['valves'][0]
                        self.pump_or_valve = 'valve'
                        adj_links = NetworkUtils.find_links_adjacent_to_link(self.params, self.params.valves_vlay, self.pump_valve_ft, False, True, True)
                    else:
                        return

                    pump_valve_pts = self.pump_valve_ft.geometry().asPolyline()

                    # self.rubber_bands_d[0] = (self.build_rubber_band(pump_valve_pts), range(len(pump_valve_pts)), pump_valve_pts)
                    self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()])

                    rb_index = 1
                    if 'pipes' in adj_links:
                        for adj_link_ft in adj_links['pipes']:
                            self.process_adj(adj_link_ft, self.params.pipes_vlay, rb_index)
                            rb_index += 1
                    if 'pumps' in adj_links:
                        for adj_link_ft in adj_links['pumps']:
                            self.process_adj(adj_link_ft, self.params.pumps_vlay, rb_index)
                            rb_index += 1
                    if 'valves' in adj_links:
                        for adj_link_ft in adj_links['valves']:
                            self.process_adj(adj_link_ft, self.params.valves_vlay, rb_index)
                            rb_index += 1

                    # Find the nodes adjacent to the pump/valve
                    self.adj_junctions = NetworkUtils.find_start_end_nodes_w_layer(self.params, self.pump_valve_ft.geometry())

    def process_adj(self, adj_link_ft, layer, rb_index):
        adj_link_pts = adj_link_ft.geometry().asPolyline()
        pump_valve_pts = self.pump_valve_ft.geometry().asPolyline()

        if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[0]), adj_link_pts[0], self.params.tolerance):
            self.adj_links_fts_d[adj_link_ft] = (0, layer)
            # rb_pts = [pump_valve_pts[0], adj_link_pts[1]]
            # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[0]])

        if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[-1]), adj_link_pts[0], self.params.tolerance):
            self.adj_links_fts_d[adj_link_ft] = (0, layer)
            # rb_pts = [pump_valve_pts[-1], adj_link_pts[1]]
            # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[-1]])

        if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[0]), adj_link_pts[-1], self.params.tolerance):
            self.adj_links_fts_d[adj_link_ft] = (len(adj_link_pts)-1, layer)
            # rb_pts = [pump_valve_pts[0], adj_link_pts[-2]]
            # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[0]])

        if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[-1]), adj_link_pts[-1], self.params.tolerance):
            self.adj_links_fts_d[adj_link_ft] = (len(adj_link_pts)-1, layer)
            # rb_pts = [pump_valve_pts[-1], adj_link_pts[-2]]
            # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[-1]])

    def canvasMoveEvent(self, event):

        self.mouse_pt = self.toMapCoordinates(event.pos())

        elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1)
        if elev is not None:
            self.elev = elev
            self.dock_widget.lbl_elev_val.setText("{0:.2f}".format(self.elev))
        else:
            self.elev = None
            self.dock_widget.lbl_elev_val.setText('-')

        # Mouse not clicked
        if not self.mouse_clicked:

            match = self.snapper.snapToMap(self.mouse_pt)
            if match.isValid():

                self.snap_results = match
                # snapped_pt = self.snap_results[0].snappedVertex
                snapped_vertex = match.point()

                self.vertex_marker.setCenter(QgsPoint(snapped_vertex.x(), snapped_vertex.y()))
                self.vertex_marker.setColor(QColor(255, 0, 0))
                self.vertex_marker.setIconSize(10)
                self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE)  # or ICON_CROSS, ICON_X
                self.vertex_marker.setPenWidth(3)
                self.vertex_marker.show()
            else:
                self.snap_results = None
                self.selected_node_ft = None
                self.vertex_marker.hide()

        # Mouse clicked
        else:

            # Update rubber band
            if self.snap_results is not None and self.rubber_band:

                snapped_pt = self.snap_results.point()

                # In 2.16+: self.delta_vec = QgsVector(self.mouse_pt - snapped_pt)
                self.delta_vec = QgsVector(self.mouse_pt.x() - snapped_pt.x(),
                                           self.mouse_pt.y() - snapped_pt.y())

                self.move_rubber_band_pt(self.rubber_band)

                # if self.adj_links_fts is None or (not self.adj_links_fts['pipes'] and not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']):
                #     # There are no adjacent links
                #     self.move_rubber_band_pt(self.rubber_bands_d[0])
                # else:
                #
                #     # It's just a junction
                #     if not self.pump_valve_selected:
                #         for key, value in self.rubber_bands_d.iteritems():
                #             self.move_rubber_band_pt(self.rubber_bands_d[key])
                #
                #     # It's a pump/valve
                #     else:
                #
                #         # Adjacent links are neither pumps nor valves: find the two pipes adjacent to the node
                #         # Or node adjacent to pump or valve and NOT using block logic
                #         if (not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']) or not self.params.block_logic:
                #
                #             for key, value in self.rubber_bands_d.iteritems():
                #                 self.move_rubber_band_pt(self.rubber_bands_d[key])
                #                 # in 2.16: self.rubber_bands_d[key].movePoint(1, snapped_pt + self.delta_vec)
                #                 # self.rubber_bands_d[key].movePoint(2, QgsPoint(snapped_pt.x() + self.delta_vec.x(), snapped_pt.y() + self.delta_vec.y()))
                #
                #         # Node adjacent to pump or valve and using block logic
                #         else:
                #             for key, value in self.rubber_bands_d.iteritems():
                #                 self.move_rubber_band_pt(self.rubber_bands_d[key])

    def move_rubber_band_pt(self, rubber_band_v):
        # rubber_band = rubber_band_v[0]
        # pt_indices = rubber_band_v[1]
        # start_pts = rubber_band_v[2]

        # for i in range(len(pt_indices)):
        #     rubber_band.movePoint(pt_indices[i], QgsPoint(start_pts[i].x() + self.delta_vec.x(), start_pts[i].y() + self.delta_vec.y()))

        rubber_band_v.movePoint(1, QgsPoint(self.clicked_pt.x() + self.delta_vec.x(), self.clicked_pt.y() + self.delta_vec.y()))

    def canvasReleaseEvent(self, event):

        mouse_pt = self.toMapCoordinates(event.pos())

        if not self.mouse_clicked:
            return

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

            if self.snap_results is not None:

                snap_results = self.snap_results
                selected_node_ft = self.selected_node_ft
                selected_node_ft_lay = self.selected_node_ft_lay

                # Check elev
                if self.elev is None and self.params.dem_rlay is not None:
                    self.iface.messageBar().pushMessage(
                        Parameters.plug_in_name,
                        'Elevation value not available: element elevation set to 0.',
                        QgsMessageBar.WARNING,
                        5)  # TODO: softcode

                # It's just a pipe vertex
                if selected_node_ft is None:

                    feat = vector_utils.get_feats_by_id(snap_results.layer(), snap_results.featureId())
                    vertex_id = QgsVertexId(0, 0, snap_results.vertexIndex(), QgsVertexId.SegmentVertex)
                    vertex_v2 = feat[0].geometry().geometry().vertexAt(vertex_id)
                    new_pos_pt_v2 = QgsPointV2(mouse_pt.x(), mouse_pt.y())
                    new_pos_pt_v2.addZValue(vertex_v2.z())
                    LinkHandler.move_link_vertex(self.params, self.params.pipes_vlay, feat[0], new_pos_pt_v2,
                                                 snap_results.vertexIndex())

                # There are adjacent links: it's a node
                else:

                    # Not pump or valve: plain junction
                    if not self.pump_valve_selected:

                        # Update junction geometry
                        NodeHandler.move_element(
                            selected_node_ft_lay,
                            self.params.dem_rlay,
                            selected_node_ft,
                            mouse_pt)

                        # Update pipes
                        for feat, (vertex_index, layer) in self.adj_links_fts_d.iteritems():

                            vertex_id = QgsVertexId(0, 0, vertex_index, QgsVertexId.SegmentVertex)
                            vertex_v2 = feat.geometry().geometry().vertexAt(vertex_id)
                            new_pos_pt_v2 = QgsPointV2(mouse_pt.x(), mouse_pt.y())
                            new_pos_pt_v2.addZValue(vertex_v2.z())

                            LinkHandler.move_link_vertex(self.params, layer, feat, new_pos_pt_v2, vertex_index)

                    # Pump or valve
                    else:

                        # Update junctions geometry
                        NodeHandler.move_element(self.adj_junctions[0][1],
                                                 self.params.dem_rlay,
                                                 self.adj_junctions[0][0],
                                                 QgsPoint(
                                                    self.adj_junctions[0][0].geometry().asPoint().x() + self.delta_vec.x(),
                                                    self.adj_junctions[0][0].geometry().asPoint().y() + self.delta_vec.y()))

                        NodeHandler.move_element(self.adj_junctions[1][1],
                                                 self.params.dem_rlay,
                                                 self.adj_junctions[1][0],
                                                 QgsPoint(
                                                     self.adj_junctions[1][0].geometry().asPoint().x() + self.delta_vec.x(),
                                                     self.adj_junctions[1][0].geometry().asPoint().y() + self.delta_vec.y()))

                        # in 2.16: NodeHandler.move_element(Parameters.junctions_vlay, self.adj_links_fts[0], self.adj_links_fts[0].geometry().asPoint() + self.delta_vec)

                        if self.pump_or_valve == 'pump':
                            lay = self.params.pumps_vlay
                        elif self.pump_or_valve == 'valve':
                            lay = self.params.valves_vlay

                        # Move the pump/valve
                        LinkHandler.move_pump_valve(lay, self.pump_valve_ft, self.delta_vec)

                        # Move the adjacent pipes' vertices
                        for feat, (vertex_index, layer) in self.adj_links_fts_d.iteritems():

                            vertex_id = QgsVertexId(0, 0, vertex_index, QgsVertexId.SegmentVertex)
                            vertex_v2 = feat.geometry().geometry().vertexAt(vertex_id)
                            new_pos_pt_v2 = QgsPointV2(
                                feat.geometry().vertexAt(vertex_index).x() + self.delta_vec.x(),
                                feat.geometry().vertexAt(vertex_index).y() + self.delta_vec.y())
                            new_pos_pt_v2.addZValue(vertex_v2.z())

                            LinkHandler.move_link_vertex(self.params, layer, feat, new_pos_pt_v2, vertex_index)
                            # In 2.16: LinkHandler.move_pipe_vertex(feat, feat.geometry().vertexAt(vertex_index) + self.delta_vec, vertex_index)

                    self.adj_links_fts_d.clear()

                symbology.refresh_layer(self.iface.mapCanvas(), self.params.junctions_vlay)
                symbology.refresh_layer(self.iface.mapCanvas(), self.params.reservoirs_vlay)
                symbology.refresh_layer(self.iface.mapCanvas(), self.params.tanks_vlay)
                symbology.refresh_layer(self.iface.mapCanvas(), self.params.pipes_vlay)
                symbology.refresh_layer(self.iface.mapCanvas(), self.params.pumps_vlay)
                symbology.refresh_layer(self.iface.mapCanvas(), self.params.valves_vlay)

            # Remove vertex marker and rubber band
            self.vertex_marker.hide()
            self.iface.mapCanvas().scene().removeItem(self.rubber_band)

    def activate(self):

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

        # Snapping
        layers = {
            self.params.junctions_vlay: QgsPointLocator.Vertex,
            self.params.reservoirs_vlay: QgsPointLocator.Vertex,
            self.params.tanks_vlay: QgsPointLocator.Vertex,
            self.params.pipes_vlay: QgsPointLocator.Vertex}
        self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance)

        # Editing
        if not self.params.junctions_vlay.isEditable():
            self.params.junctions_vlay.startEditing()
        if not self.params.reservoirs_vlay.isEditable():
            self.params.reservoirs_vlay.startEditing()
        if not self.params.tanks_vlay.isEditable():
            self.params.tanks_vlay.startEditing()
        if not self.params.pipes_vlay.isEditable():
            self.params.pipes_vlay.startEditing()
        if not self.params.pumps_vlay.isEditable():
            self.params.pumps_vlay.startEditing()
        if not self.params.valves_vlay.isEditable():
            self.params.valves_vlay.startEditing()

    def deactivate(self):
        # self.rubber_bands.clear()
        self.canvas().scene().removeItem(self.vertex_marker)
        self.dock_widget.btn_move_element.setChecked(False)

    def isZoomTool(self):
        return False

    def isTransient(self):
        return False

    def isEditTool(self):
        return True

    def build_rubber_band(self, points):
        rubber_band = QgsRubberBand(self.canvas(), False)  # False = not a polygon
        rubber_band.setToGeometry(QgsGeometry.fromPolyline(points), None)
        # for point in points:
        #     rubber_band.addPoint(point)
        rubber_band.setColor(QColor(255, 128, 128))
        rubber_band.setWidth(1)
        rubber_band.setBrushStyle(Qt.Dense4Pattern)
        return rubber_band
Beispiel #4
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()